Skip to content

Instantly share code, notes, and snippets.

@ChaseTheLight01
Created March 24, 2024 06:13
Show Gist options
  • Save ChaseTheLight01/d61a9fd3791763e8b487961bf75887de to your computer and use it in GitHub Desktop.
Save ChaseTheLight01/d61a9fd3791763e8b487961bf75887de to your computer and use it in GitHub Desktop.
LightChaserV3_Cantina_Goat

LightChaser-V3

Generated for: Cantina : Goat Tech

Generated on: 2024-03-24

Total findings: 155

Total HIGH findings: 0

Total Medium findings: 4

Total Low findings: 36

Total Gas findings: 49

Total Refactoring findings: 0

Total NonCritical findings: 66

Total Disputed findings: 0

Summary for Medium findings

Number Details Instances
[Medium-1] Use call instead of transfer on payable addresses 4
[Medium-2] Using block.timestamp as the deadline/expiry invites MEV 3
[Medium-3] isContract is not a reliable way to determine if a address is a EOA or contract 2
[MEDIUM-4] Privileged functions can create points of failure 43

Summary for Low findings

Number Details Instances
[Low-1] Potential division by zero should have zero checks in place 6
[Low-2] Division operations should always be performed after multiplication operations 1
[Low-3] Missing checks for address(0x0) when updating address state variables 7
[Low-4] Missing empty bytes check when assigning bytes/string state variable 1
[Low-5] Contracts with multiple onlyXYZ modifiers where XYZ is a role can introduce complexities when managing privileges 1
[Low-6] Greater than comparisons made on state uints that can be set to max 3
[Low-7] Some tokens may revert when zero value transfers are made 4
[Low-8] Return values not checked for approve() 4
[Low-9] Token burning is centralized 1
[Low-10] Token minting is centralized 1
[Low-11] Low level calls in solidity versions preceding 0.8.14 can result in an optimiser bug 2
[Low-12] Empty receive functions can cause gas issues 1
[Low-13] Ownable2Step should be used in place of Ownable 1
[Low-14] The call send() should almost never be used as instead of throwing an error it returns false. Use transfer( instead 1
[Low-15] Upgradable contracts should have a __gap variable 4
[Low-16] No limits when setting min/max amounts 1
[Low-17] Initializer function can be front run 14
[Low-18] Use _disableInitializers() to ensure initialization occurs once 4
[Low-19] Minting to the zero address should be avoided 1
[Low-20] Loss of precision 16
[Low-21] Use of onlyOwner functions can be lost 1
[Low-22] Constant decimal values 3
[Low-23] Revert on Transfer to the Zero Address 6
[Low-24] Critical functions should have a timelock 3
[Low-25] Use of abi.encodePacked with dynamic types inside keccak256 1
[Low-26] Inconsistent expiry logic using block global values 1
[Low-27] Vulnerable version of openzeppelin contracts used 1
[Low-28] Division in comparison 1
[Low-29] Contract contains payable functions but no withdraw/sweep function 2
[Low-30] Consider the case where totalSupply is zero 2
[Low-31] Unsafe use of transfer()/transferFrom() with IERC20 4
[Low-32] Large transfers may not work with some ERC20 tokens 4
[Low-33] Functions calling contracts/addresses with transfer hooks are missing reentrancy guards 8
[Low-34] Off-by-one timestamp error 2
[Low-35] Incorrect withdraw declaration 3
[Low-36] Create methods are suspicious of the reorg attack 1

Summary for NonCritical findings

Number Details Instances
[NonCritical-1] Some if-statement can be converted to a ternary 48
[NonCritical-2] Addresses shouldn't be hard-coded 4
[NonCritical-3] Subtraction may underflow if multiplication is too large 1
[NonCritical-4] Avoid commenting out unused code in production 1
[NonCritical-5] Code does not follow the best practice of check-effects-interaction 1
[NonCritical-6] Events may be emitted out of order due to code not follow the best practice of check-effects-interaction 1
[NonCritical-7] Unnecessary struct attribute prefix 1
[NonCritical-8] Contracts should have all public/external functions exposed by interfaces 122
[NonCritical-9] Using abi.encodePacked can result in hash collision when used in hashing functions 1
[NonCritical-10] Non constant/immutable state variables are missing a setter post deployment 3
[NonCritical-11] Require statements should have error string 1
[NonCritical-12] For loops in public or external functions should be avoided due to high gas costs and possible DOS 2
[NonCritical-13] Using zero as a parameter 7
[NonCritical-14] Consider implementing two-step procedure for updating protocol addresses 13
[NonCritical-15] Library has public/external functions 1
[NonCritical-16] Interfaces should be declared in a separate file 1
[NonCritical-17] Events regarding state variable changes should emit the previous state variable value 12
[NonCritical-18] In functions which accept an address as a parameter, there should be a zero address check to prevent bugs 114
[NonCritical-19] Enum values should be used in place of constant array indexes 24
[NonCritical-20] Default bool values are manually set 2
[NonCritical-21] Revert statements within external and public functions can be used to perform DOS attacks 1
[NonCritical-22] Functions which are either private or internal should have a preceding _ in their name 28
[NonCritical-23] Public state variables shouldn't have a preceding _ in their name 22
[NonCritical-24] Contract lines should not be longer than 120 characters for readability 5
[NonCritical-25] Avoid updating storage when the value hasn't changed 10
[NonCritical-26] Specific imports should be used where possible so only used code is imported 41
[NonCritical-27] Old Solidity version 1
[NonCritical-28] Not all event definitions are utilizing indexed variables. 22
[NonCritical-29] uint/int variables should have the bit size defined explicitly 320
[NonCritical-30] Functions with array parameters should have length checks in place 2
[NonCritical-31] Interface imports should be declared first 13
[NonCritical-32] Multiple mappings can be replaced with a single struct mapping 3
[NonCritical-33] Unused state variables present 2
[NonCritical-34] Constants should be on the left side of the comparison 23
[NonCritical-35] Use a single file for system wide constants 5
[NonCritical-36] Return values of transfer()/transferFrom() not checked 14
[NonCritical-37] Consider using modifiers for address control 1
[NonCritical-38] Use of non-named numeric constants 6
[NonCritical-39] Long powers of ten should use scientific notation 1eX 4
[NonCritical-40] Redundant else statement 2
[NonCritical-41] Inconsistent usage of int/uint with int256/uint256 in contract/abstract/library/interface 8
[NonCritical-42] Employ Explicit Casting to Bytes or Bytes32 for Enhanced Code Clarity and Meaning 1
[NonCritical-43] Unused structs present 1
[NonCritical-44] Non-assembly method available 2
[NonCritical-45] Empty bytes check is missing 1
[NonCritical-46] No equate comparison checks between to and from address parameters 4
[NonCritical-47] Top level declarations should be separated by two blank lines 58
[NonCritical-48] Revert should be used on some functions instead of return 1
[NonCritical-49] No access control on receive/payable fallback 1
[NonCritical-50] Unused events present 1
[NonCritical-51] Missing events in sensitive functions 10
[NonCritical-52] Ensure block.timestamp is only used in long time intervals 2
[NonCritical-53] Avoid mutating function parameters 1
[NonCritical-54] Contracts with only unimplemented functions can be labeled as abstract 17
[NonCritical-55] Incorrect bool naming convention 1
[NonCritical-56] Consider only defining one library/interface/contract per sol file 3
[NonCritical-57] Immutable and constant integer state variables should not be casted 12
[NonCritical-58] Empty revert statement 1
[NonCritical-59] Long numbers should include underscores to improve readability and prevent typos 4
[NonCritical-60] Constant block.timestamp range 1
[NonCritical-61] Use 'using' keyword when using specific imports rather than calling the specific import directly 78
[NonCritical-62] Avoid declaring variables with the names of defined functions within the project 4
[NonCritical-63] Constructors should emit an event 1
[NonCritical-64] Consider using AccessControlDefaultAdminRules rather than AccessControl 2
[NonCritical-65] Avoid using 'owner' or '_owner' as a parameter name 4
[NonCritical-66] Memory-safe annotation missing 1

Summary for Gas findings

Number Details Instances Gas
[Gas-1] State variables used within a function more than once should be cached to save gas 5 5500
[Gas-2] The result of a function call should be cached rather than re-calling the function 1 300
[Gas-3] Multiple accesses of the same mapping/array key/index should be cached 9 6048
[Gas-4] Shortcircuit rules can be be used to optimize some gas usage 1 10500
[Gas-5] Use assembly to write address/contract storage values 71 0.0
[Gas-6] x + y is more efficient than using += for state variables (likewise for -=) 3 45
[Gas-7] Public functions not used internally can be marked as external to save gas 11 0.0
[Gas-8] Calldata should be used in place of memory function parameters when not mutated 1 13
[Gas-9] Usage of smaller uint/int types causes overhead 13 9295
[Gas-10] Use != 0 instead of > 0 14 588
[Gas-11] Integer increments by one can be unchecked to save on gas fees 7 5880
[Gas-12] Use byte32 in place of string 6 0.0
[Gas-13] Default bool values are manually reset 4 0.0
[Gas-14] Default int values are manually reset 8 0.0
[Gas-15] Mappings used within a function more than once should be cached to save gas 1 100
[Gas-16] Use assembly to check for the zero address 3 0.0
[Gas-17] Some error strings are not descriptive 2 0.0
[Gas-18] Divisions which do not divide by -X cannot overflow or underflow so such operations can be unchecked to save gas 16 0.0
[Gas-19] Can transfer 0 13 0.0
[Gas-20] Redundant state variable getters 1 0.0
[Gas-21] State variables which are not modified within functions should be set as constants or immutable for values set at deployment 3 0.0
[Gas-22] Divisions of powers of 2 can be replaced by a right shift operation to save gas 2 0.0
[Gas-23] Use bitmap to save gas 20 28000
[Gas-24] Consider using OZ EnumerateSet in place of nested mappings 1 1000
[Gas-25] Use selfBalance() in place of address(this).balance 7 39200
[Gas-26] Using private rather than public for constants and immutables, saves gas 6 0.0
[Gas-27] Identical Deployments Should be Replaced with Clone 1 0.0
[Gas-28] Use assembly to validate msg.sender 1 0.0
[Gas-29] Simple checks for zero uint can be done using assembly to save gas 8 384
[Gas-30] Use Unchecked for Divisions on Constant or Immutable Values 6 0.0
[Gas-31] Using nested if to save gas 8 384
[Gas-32] Stack variable cost less than state variables while used in emiting event 3 81
[Gas-33] Stack variable cost less than mappings while used in emiting event 2 36
[Gas-34] Caching global variables is more expensive than using the actual variable 4 192
[Gas-35] Inline modifiers used only once 2 0.0
[Gas-36] ++X costs slightly less gas than X++ (same with --) 4 80
[Gas-37] Solidity versions 0.8.19 and above are more gas efficient 1 2000
[Gas-38] Variables that can be set to immutable 2 0.0
[Gas-39] Internal functions only used once can be inlined to save gas 26 20280
[Gas-40] Constructors can be marked as payable to save deployment gas 3 0.0
[Gas-41] Same cast is done multiple times 7 0.0
[Gas-42] Assigning to structs can be more efficient 5 3250
[Gas-43] Internal functions never used once can be removed 2 0.0
[Gas-44] Only emit event in setter function if the state variable was changed 9 0.0
[Gas-45] It is a waste of GAS to emit variable literals 1 8
[Gas-46] Use OZ Array.unsafeAccess() to avoid repeated array length checks 6 75600
[Gas-47] Call msg.XYZ directly rather than caching the value to save gas 4 0.0
[Gas-48] Use of memory instead of storage for struct/array state variables 4 33600
[Gas-49] Public functions not called internally 11 0.0

[Medium-1] Use call instead of transfer on payable addresses

Resolution

In Solidity, when transferring Ether, .transfer() and .send() are commonly used. However, they have a limitation: they forward only a stipend of 2300 gas, which isn't enough to execute any code in the recipient contract beyond a simple event emission. Thus, if the recipient is a contract, the transfer may fail unexpectedly.

To overcome this, Solidity introduced the .call{value: _amount}("") method, which forwards all available gas and can invoke more complex functionality. It's also safer in that it does not revert on failure but instead returns a boolean value to indicate success or failure. Therefore, it is generally a better choice to use .call when transferring Ether to a payable address, with the necessary safety checks implemented to handle potential errors.

Num of instances: 4

Findings

Click to show findings

['478']

478:     function earningPulls(
479:         address account_,
480:         address[] memory poolOwners_,
481:         address bountyPullerTo_
482:     )
483:         public
484:         tryPublicMint
485:     {
486:         _dEarning.clean();
487:         _eEarning.clean();
488: 
489:         
490:         _dP2PDistributor.distribute();
491: 
492:         
493:         
494: 
495:         
496:         
497: 
498:         _distributorClaimFor(address(_eDP2PDistributor), account_, address(this));
499:         _distributorClaimFor(address(_dDP2PDistributor), account_, address(_dEarning));
500: 
501:         for(uint i = 0; i < poolOwners_.length; i++) {
502:             _earningPull(account_, poolOwners_[i]);
503:         }
504: 
505:         if (bountyPullerTo_ == account_) {
506:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
507:         } else {
508:             uint256 amountForPuller = _geth.balanceOf(address(this)) * _bountyPullEarningPercent / LPercentage.DEMI;
509: 
510:             LLido.sellWsteth(amountForPuller);
511:             LLido.wethToEth();
512:             payable(bountyPullerTo_).transfer(address(this).balance); // <= FOUND
513: 
514:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
515:         }
516: 
517:         _dEarning.update(account_, true);
518:         _eEarning.update(account_, true);
519: 
520:         _reCalFs(account_);
521:     }

['721']

721:     function claimRevenueShareDevTeam()
722:         public
723:     {
724:         address account = msg.sender;
725:         earningWithdrawDevTeam();
726:         _devTeam.claimFor(account, address(this));
727: 
728:         LLido.allToEth(0);
729:         payable(account).transfer(address(this).balance); // <= FOUND
730:     }

['478']

478:     function earningPulls(
479:         address account_,
480:         address[] memory poolOwners_,
481:         address bountyPullerTo_
482:     )
483:         public
484:         tryPublicMint
485:     {
486:         _dEarning.clean();
487:         _eEarning.clean();
488: 
489:         
490:         _dP2PDistributor.distribute();
491: 
492:         
493:         
494: 
495:         
496:         
497: 
498:         _distributorClaimFor(address(_eDP2PDistributor), account_, address(this));
499:         _distributorClaimFor(address(_dDP2PDistributor), account_, address(_dEarning));
500: 
501:         for(uint i = 0; i < poolOwners_.length; i++) {
502:             _earningPull(account_, poolOwners_[i]);
503:         }
504: 
505:         if (bountyPullerTo_ == account_) {
506:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
507:         } else {
508:             uint256 amountForPuller = _geth.balanceOf(address(this)) * _bountyPullEarningPercent / LPercentage.DEMI;
509: 
510:             LLido.sellWsteth(amountForPuller);
511:             LLido.wethToEth(); // <= FOUND
512:             payable(bountyPullerTo_).transfer(address(this).balance); // <= FOUND
513: 
514:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
515:         }
516: 
517:         _dEarning.update(account_, true);
518:         _eEarning.update(account_, true);
519: 
520:         _reCalFs(account_);
521:     }

['721']

721:     function claimRevenueShareDevTeam()
722:         public
723:     {
724:         address account = msg.sender;
725:         earningWithdrawDevTeam();
726:         _devTeam.claimFor(account, address(this));
727: 
728:         LLido.allToEth(0); // <= FOUND
729:         payable(account).transfer(address(this).balance); // <= FOUND
730:     }

[Medium-2] Using block.timestamp as the deadline/expiry invites MEV

Resolution

Using block.timestamp for operation deadlines can be exploited by malicious entities in the Miner Extractable Value (MEV) landscape. Instead of ensuring immediacy, this approach lets miners choose when to execute the transaction within the block, potentially manipulating outcomes for their gain. For instance, they might delay the transaction until market conditions shift, triggering maximum allowable slippage or even causing other significant events like liquidations. To mitigate this risk, deadlines should be determined off-chain and explicitly set by the transaction initiator, ensuring a more predictable and secure execution environment and reducing potential MEV opportunities.

Num of instances: 3

Findings

Click to show findings

['298']

270:     function mintNewPosition(
271:         uint wethA_,
272:         uint wstethA_
273:     )
274:         internal
275:         returns
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1)
277:     {
278:         weth.approve(address(nonfungiblePositionManager), wethA_);
279:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
280: 
281:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth);
282:         address token1 = token0 == address(weth) ? address(wsteth) : address(weth);
283:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_;
284:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_;
285: 
286:         INonfungiblePositionManager.MintParams
287:             memory params = INonfungiblePositionManager.MintParams({
288:                 token0: token0,
289:                 token1: token1,
290:                 fee: POOL_FEE,
291:                 tickLower: (MIN_TICK / TICK_SPACING) * TICK_SPACING,
292:                 tickUpper: (MAX_TICK / TICK_SPACING) * TICK_SPACING,
293:                 amount0Desired: amount0ToAdd,
294:                 amount1Desired: amount1ToAdd,
295:                 amount0Min: 0,
296:                 amount1Min: 0,
297:                 recipient: address(this),
298:                 deadline: block.timestamp // <= FOUND
299:             });
300: 
301:         (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(
302:             params
303:         );
304:     }

['327']

306:     function increaseLiquidityCurrentRange(
307:         uint tokenId_,
308:         uint wethA_,
309:         uint wstethA_
310:     ) internal returns (uint128 liquidity, uint amount0, uint amount1) {
311: 
312:         weth.approve(address(nonfungiblePositionManager), wethA_);
313:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
314: 
315:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth);
316:         
317:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_;
318:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_;
319: 
320:         INonfungiblePositionManager.IncreaseLiquidityParams
321:             memory params = INonfungiblePositionManager.IncreaseLiquidityParams({
322:                 tokenId: tokenId_,
323:                 amount0Desired: amount0ToAdd,
324:                 amount1Desired: amount1ToAdd,
325:                 amount0Min: 0,
326:                 amount1Min: 0,
327:                 deadline: block.timestamp // <= FOUND
328:             });
329: 
330:         (liquidity, amount0, amount1) = nonfungiblePositionManager.increaseLiquidity(
331:             params
332:         );
333:     }

['375']

360:     function decreaseLiquidityCurrentRange(
361:         uint tokenId_,
362:         uint128 decLiqA_
363:     )
364:         internal
365:         returns (uint amount0, uint amount1)
366:     {
367:         
368:         
369:         INonfungiblePositionManager.DecreaseLiquidityParams
370:             memory params = INonfungiblePositionManager.DecreaseLiquidityParams({
371:                 tokenId: tokenId_,
372:                 liquidity: decLiqA_,
373:                 amount0Min: 0,
374:                 amount1Min: 0,
375:                 deadline: block.timestamp // <= FOUND
376:             });
377: 
378:         (amount0, amount1) = nonfungiblePositionManager.decreaseLiquidity(params);
379:     }

[Medium-3] isContract is not a reliable way to determine if a address is a EOA or contract

Resolution

Using isContract to distinguish between Externally Owned Accounts (EOAs) and contracts in Ethereum is no longer reliable due to proxy contracts and contract creation during transaction execution. This method may inaccurately classify contracts as EOAs or vice versa, especially post-transaction. OpenZeppelin's Address library deprecated isContract recognizing these limitations. For security and accuracy, alternatives or additional checks should be used for distinguishing account types, aligning with current best practices and avoiding false assumptions based on outdated or unreliable methods.

Num of instances: 2

Findings

Click to show findings

['32']

32:         if (balance_ > _athBalance && !isContract(account_)) { // <= FOUND

['110']

110:         require(!isContract(owner_), "owner cannot be a contract"); // <= FOUND

[MEDIUM-4] Privileged functions can create points of failure

Number of instances found

43

Resolution

Ensure such accounts are protected and consider implementing multi sig to prevent a single point of failure

Findings

Findings are labeled with ' <= FOUND'

Click to show findings

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/AccessControl.sol#L52-L56

52:     function addAdmins( // <= FOUND
53:         address[] memory accounts_
54:     )
55:         external
56:         onlyOwner // <= FOUND
57:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/AccessControl.sol#L65-L69

65:     function removeAdmins( // <= FOUND
66:         address[] memory accounts_
67:     )
68:         external
69:         onlyOwner // <= FOUND
70:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/AccessControl.sol#L80-L80

78:     function pause()
79:         external
80:         onlyOwner // <= FOUND
81:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/AccessControl.sol#L87-L87

85:     function unpause()
86:         external
87:         onlyOwner // <= FOUND
88:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/DCT.sol#L48-L48

46:     function start()
47:         external
48:         onlyOwner // <= FOUND
49:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/Locker.sol#L134-L134

130:     function updatePenaltyAddress(
131:         address penaltyAddress_
132:     )
133:         external
134:         onlyOwner // <= FOUND
135:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/PERC20.sol#L150-L150

146:     function setInPrivateMode(
147:         bool inPrivateMode_
148:     )
149:         external
150:         onlyOwner // <= FOUND
151:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/PrivateVester.sol#L19-L19

14:     function config(
15:         IVester vester_,
16:         IERC20 token_
17:     )
18:         external
19:         onlyOwner // <= FOUND
20:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/PrivateVester.sol#L47-L47

41:     function transferLock(
42:         address from_,
43:         address to_,
44:         uint amount_
45:     )
46:         external
47:         onlyOwner // <= FOUND
48:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/PrivateVester.sol#L68-L68

63:     function withdraw(
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner // <= FOUND
69:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/PrivateVester.sol#L81-L81

73:     function sendTos(
74:         address[] memory investers_,
75:         uint[] memory amounts_,
76:         uint[] memory vestingDurs_,
77:         uint[] memory vestingPercents_,
78:         uint[] memory cliffs_
79:     )
80:         external
81:         onlyOwner // <= FOUND
82:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/Controller.sol#L737-L737

733:     function updateConfigs(
734:         uint[] memory values_
735:     )
736:         external
737:         onlyAdmin // <= FOUND
738:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/DCT.sol#L148-L148

144:     function changeRewardPool(
145:         address rewardPool_
146:     )
147:         external
148:         onlyAdmin // <= FOUND
149:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/Distributor.sol#L143-L143

138:     function claimFor(
139:         address account_,
140:         address dest_
141:     )
142:         external
143:         onlyAdmin // <= FOUND
144:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/Earning.sol#L68-L68

63:     function updateMaxEarning(
64:         address account_,
65:         uint maxEarning_
66:     )
67:         external
68:         onlyAdmin // <= FOUND
69:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/Earning.sol#L123-L123

117:     function withdraw(
118:         address account_,
119:         uint amount_,
120:         address dest_
121:     )
122:         external
123:         onlyAdmin // <= FOUND
124:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/EthSharing.sol#L124-L129

122:     function configSystem(
123:         uint devTeamPercent_,
124:         uint defaultOwnerPercent_, // <= FOUND
125:         uint defaultUserPercent_,
126:         bool inDefaultOnlyMode_
127:     )
128:         external
129:         onlyAdmin // <= FOUND
130:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/EthSharing.sol#L140-L143

139:     function tryResetPool(
140:         address poolOwner_ // <= FOUND
141:     )
142:         external
143:         onlyAdmin // <= FOUND
144:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/EthSharing.sol#L151-L154

150:     function initPoolConfig(
151:         address poolOwner_ // <= FOUND
152:     )
153:         external
154:         onlyAdmin // <= FOUND
155:         returns(bool)
156:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/Locker.sol#L61-L65

59:     function lock(
60:         address account_,
61:         address poolOwner_, // <= FOUND
62:         uint duration_
63:     )
64:         external
65:         onlyAdmin // <= FOUND
66:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/Locker.sol#L76-L82

74:     function withdraw(
75:         address account_,
76:         address poolOwner_, // <= FOUND
77:         address dest_,
78:         uint amount_,
79:         bool isForced_
80:     )
81:         external
82:         onlyApprovedAdmin(account_) // <= FOUND
83:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/PERC20.sol#L84-L84

82:     function activeAthRecord()
83:         external
84:         onlyAdmin // <= FOUND
85:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/PERC20.sol#L91-L91

89:     function deactiveAthRecord()
90:         external
91:         onlyAdmin // <= FOUND
92:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/PERC20.sol#L160-L160

155:     function mint(
156:         address account_,
157:         uint amount_
158:     )
159:         external
160:         onlyAdmin // <= FOUND
161:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/PERC20.sol#L171-L171

165:     function burn(
166:         address account_,
167:         uint amount_
168:     )
169:         external
170:         virtual
171:         onlyAdmin // <= FOUND
172:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/PoolFactory.sol#L108-L108

104:     function createPool(
105:         address owner_
106:     )
107:         external
108:         onlyAdmin // <= FOUND
109:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/Profile.sol#L71-L71

66:     function updateSponsor(
67:         address account_,
68:         address sponsor_
69:     )
70:         external
71:         onlyAdmin // <= FOUND
72:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/Profile.sol#L106-L106

102:     function setDefaultSPercentConfig(
103:         uint sPercent_
104:     )
105:         external
106:         onlyAdmin // <= FOUND
107:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/Profile.sol#L116-L116

112:     function setMinSPercentConfig(
113:         uint sPercent_
114:     )
115:         external
116:         onlyAdmin // <= FOUND
117:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/Profile.sol#L127-L127

122:     function updateFsOf(
123:         address account_,
124:         uint fs_
125:     )
126:         external
127:         onlyAdmin // <= FOUND
128:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/Profile.sol#L139-L139

134:     function updateBoosterOf(
135:         address account_,
136:         uint booster_
137:     )
138:         external
139:         onlyAdmin // <= FOUND
140:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/Vester.sol#L41-L41

35:     function lock(
36:         address account_,
37:         uint duration_,
38:         uint cliff_
39:     )
40:         external
41:         onlyAdmin // <= FOUND
42:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/modules/Vester.sol#L58-L58

52:     function transferLock(
53:         address from_,
54:         address to_,
55:         uint amount_
56:     )
57:         external
58:         onlyAdmin // <= FOUND
59:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/Voting.sol#L150-L150

136:     function createVote(
137:         address attacker_,
138:         address defender_,
139: 
140:         uint aEthValue_,
141:         uint dEthValue_,
142: 
143:         uint voterPercent_,
144:         uint aQuorum_,
145: 
146:         uint startedAt_,
147:         uint endAt_
148:     )
149:         external
150:         onlyAdmin // <= FOUND
151:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/Voting.sol#L302-L302

297:     function claimFor(
298:         uint voteId_,
299:         address voter_
300:     )
301:         external
302:         onlyAdmin // <= FOUND
303:     

https://github.com/cantina-forks/goattechlabs-smart-contracts/tree/main/contracts/Voting.sol#L332-L332

327:     function closeVote(
328:         uint voteId_,
329:         address dest_
330:     )
331:         external
332:         onlyAdmin // <= FOUND
333:     

[Low-1] Potential division by zero should have zero checks in place

Resolution

Implement a zero address check for found instances

Num of instances: 6

Findings

Click to show findings

['198']

198:     function _updateSponsor(
199:         address payable poolOwner_,
200:         address staker_,
201:         uint minSPercent_
202:     )
203:         internal
204:     {
205:         if (poolOwner_ == staker_) {
206:             return;
207:         }
208:         IProfile.SProfile memory profile = _profileC.profileOf(poolOwner_);
209:         if (profile.sponsor == staker_) {
210:             return;
211:         }
212:         require(profile.nextSPercent >= minSPercent_, "profile rate changed");
213:         IPoolFactory.SPool memory pool = _poolFactory.getPool(poolOwner_);
214:         IDToken p2UDtoken = IDToken(pool.dToken);
215:         uint timeDiff = block.timestamp - profile.updatedAt;
216:         if (timeDiff > _maxSponsorAfter) {
217:             timeDiff = _maxSponsorAfter;
218:         }
219: 
220:         uint sponsorDTokenBalance = p2UDtoken.balanceOf(profile.sponsor);
221:         uint stakerDTokenBalance = p2UDtoken.balanceOf(staker_);
222:         uint sponsorBonus = sponsorDTokenBalance * (_maxSponsorAdv - 1) // <= FOUND
223:             * timeDiff / _maxSponsorAfter;
224:         uint sponsorPower = sponsorDTokenBalance + sponsorBonus;
225:         if (stakerDTokenBalance > sponsorPower || poolOwner_ == profile.sponsor) {
226:             address[] memory pools = new address[](1);
227:             pools[0] = poolOwner_;
228:             earningPulls(poolOwner_, pools, poolOwner_);
229:             _profileC.updateSponsor(poolOwner_, staker_);
230:         }
231:     }

['589']

589:     function earningReinvest(
590:         bool isEth_,
591:         address payable poolOwner_,
592:         uint duration_,
593:         uint amount_,
594:         
595:         uint minSPercent_,
596:         uint poolConfigCode_
597:     )
598:         external
599:     {
600:         address account = msg.sender;
601:         if (isEth_) {
602:             LLocker.SLock memory oldLockData = _eLocker.getLockData(account, poolOwner_);
603:             uint realDuration = duration_ + LLocker.restDuration(oldLockData);
604:             if (realDuration > _maxDuration) {
605:                 realDuration = _maxDuration;
606:             }
607:             uint maxEarning = _eEarning.maxEarningOf(account);
608:             maxEarning -= amount_ * realDuration / _maxDuration; // <= FOUND
609:             _eEarning.updateMaxEarning(account, maxEarning);
610:         }
611:          earningWithdraw(isEth_, amount_, payable(address(this)), 0);
612:         _stake(isEth_, account, poolOwner_, duration_, minSPercent_, poolConfigCode_);
613:     }

['49']

49:     function calBurnStakingPower(
50:         uint powerBalance_,
51:         uint unlockedA_,
52:         uint totalLockedA_
53:     )
54:         internal
55:         pure
56:         returns(uint)
57:     {
58:         return powerBalance_ * unlockedA_ / totalLockedA_; // <= FOUND
59:     }

['103']

103:     function calAQuorum(
104:         uint aEthValue_,
105:         uint dEthValue_,
106:         uint voterPercent_,
107:         uint freezeDuration_,
108:         uint freezeDurationUnit_
109:     )
110:         internal
111:         pure
112:         returns(uint)
113:     {
114:         uint tmp = LPercentage.DEMI - voterPercent_;
115:         uint leverage = LPercentage.DEMIE2 * // <= FOUND
116:             dEthValue_ * freezeDuration_ / aEthValue_ / freezeDurationUnit_ / tmp;
117:         if (leverage < LPercentage.DEMI) {
118:             leverage = LPercentage.DEMI;
119:         }
120:         return LPercentage.DEMI * leverage / (leverage + LPercentage.DEMI);
121:     }

['87']

87:     function _withdraw(
88:         address account_,
89:         address poolOwner_,
90:         address dest_,
91:         uint amount_,
92:         bool isForced_
93:     )
94:         internal
95:     {
96:         bytes32 lockId = LLocker.getLockId(account_, poolOwner_);
97:         LLocker.SLock storage lockData = _lockData[lockId];
98:         bool isPoolOwner = account_ == poolOwner_;
99:         uint fs = _profileC.fsOf(poolOwner_);
100:         if (!isForced_) {
101:             require(LLocker.isUnlocked(lockData, fs, isPoolOwner), "not unlocked");
102:         }
103:         uint duration = LLocker.calDuration(lockData, fs, isPoolOwner);
104:         uint pastTime = block.timestamp - lockData.startedAt;
105:         if (pastTime > duration) {
106:             pastTime = duration;
107:         }
108: 
109:         lockData.amount -= amount_;
110:         uint total = amount_;
111:         uint receivedA = total * pastTime / duration; // <= FOUND
112:         _cashOut(dest_, receivedA);
113:         if (total != receivedA) {
114:             _cashOut(_penaltyAddress, total - receivedA);
115:         }
116: 
117:         emit Withdraw(account_, poolOwner_, dest_, amount_);
118:         emit UpdateLockData(account_, poolOwner_, _lockData[lockId]);
119:     }

['109']

109:     function currentLockData(
110:         address account_
111:     )
112:         public
113:         view
114:         returns(uint restA, uint restDuration)
115:     {
116:         LLocker.SLock memory lockData = _lockData[account_];
117:         restDuration = LLocker.restDuration(lockData);
118:         restA = restDuration >= lockData.duration // <= FOUND
119:             ? lockData.amount
120:             : lockData.amount * restDuration / lockData.duration;
121:     }

[Low-2] Division operations should always be performed after multiplication operations

Resolution

Perform multiplication operations first

Num of instances: 1

Findings

Click to show findings

['270']

270:     function mintNewPosition(
271:         uint wethA_,
272:         uint wstethA_
273:     )
274:         internal
275:         returns
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1)
277:     {
278:         weth.approve(address(nonfungiblePositionManager), wethA_);
279:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
280: 
281:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth);
282:         address token1 = token0 == address(weth) ? address(wsteth) : address(weth);
283:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_;
284:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_;
285: 
286:         INonfungiblePositionManager.MintParams
287:             memory params = INonfungiblePositionManager.MintParams({
288:                 token0: token0, // <= FOUND
289:                 token1: token1,
290:                 fee: POOL_FEE,
291:                 tickLower: (MIN_TICK / TICK_SPACING) * TICK_SPACING,
292:                 tickUpper: (MAX_TICK / TICK_SPACING) * TICK_SPACING,
293:                 amount0Desired: amount0ToAdd,
294:                 amount1Desired: amount1ToAdd,
295:                 amount0Min: 0,
296:                 amount1Min: 0,
297:                 recipient: address(this),
298:                 deadline: block.timestamp
299:             });
300: 
301:         (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(
302:             params
303:         );
304:     }

[Low-3] Missing checks for address(0x0) when updating address state variables

Num of instances: 7

Findings

Click to show findings

['35']

35:     function _setCleanTo(
36:         address cleanTo_
37:     )
38:         internal
39:     {
40:         _cleanTo = cleanTo_; // <= FOUND
41:         _isCleanEnabled = cleanTo_ != address(this);
42:         emit SetCleanTo(cleanTo_);
43:     }

['144']

144:     function changeRewardPool(
145:         address rewardPool_
146:     )
147:         external
148:         onlyAdmin
149:     {
150:         _rewardPool = rewardPool_; // <= FOUND
151:     }

['121']

121:     function _updatePenaltyAddress(
122:         address penaltyAddress_
123:     )
124:         internal
125:     {
126:         _penaltyAddress = penaltyAddress_; // <= FOUND
127:         emit UpdatePenaltyAddress(penaltyAddress_);
128:     }

['130']

130:     function updatePenaltyAddress(
131:         address penaltyAddress_
132:     )
133:         external
134:         onlyOwner
135:     {
136:         _updatePenaltyAddress(penaltyAddress_);
137:     }

['63']

63:     function updateMaxEarning(
64:         address account_,
65:         uint maxEarning_
66:     )
67:         external
68:         onlyAdmin
69:     {
70:         _updateMaxEarning(account_, maxEarning_);
71:     }

['66']

66:     function updateSponsor(
67:         address account_,
68:         address sponsor_
69:     )
70:         external
71:         onlyAdmin
72:     {
73:         _updateSponsor(account_, sponsor_);
74:     }

['144']

144:     function changeRewardPool(
145:         address rewardPool_
146:     )
147:         external
148:         onlyAdmin
149:     {
150:         _rewardPool = rewardPool_;
151:     }

[Low-4] Missing empty bytes check when assigning bytes/string state variable

Resolution

In programming, especially when dealing with dynamically sized types like bytes and strings in Solidity, omitting a check for empty values when assigning to a state variable can lead to unexpected behavior or vulnerabilities. This oversight may allow zero-length or null values to be stored, which could be problematic in contexts where valid data is expected. Implementing explicit checks for empty bytes or strings before assignment ensures data integrity and can prevent potential logic errors or exploits in smart contracts. It's a crucial practice in robust smart contract development to validate data thoroughly before state modifications.

Num of instances: 1

Findings

Click to show findings

['70']

58:     function initPERC20(
59:         address accessControl_,
60:         bool inPrivateMode_,
61:         string memory name_,
62:         string memory symbol_
63:     )
64:         public
65:         virtual
66:         initializer
67:     {
68:         initUseAccessControl(accessControl_);
69:         _setInPrivateMode(inPrivateMode_);
70:         _name = name_; // <= FOUND
71:         _symbol = symbol_;
72:     }

[Low-5] Contracts with multiple onlyXYZ modifiers where XYZ is a role can introduce complexities when managing privileges

Resolution

In smart contracts, using multiple onlyXYZ modifiers for different roles can complicate privilege management. OpenZeppelin's AccessControl offers a streamlined solution, enabling easier and more flexible role-based permission handling. It simplifies the assignment and revocation of roles compared to multiple individual modifiers, reducing potential errors and improving contract maintainability. This modular approach to access management makes it more straightforward to define, manage, and audit roles and permissions within a contract.

Num of instances: 1

Findings

Click to show findings

['49']

38: contract UseAccessControl is Initializable {
39:     event ApproveAdmin(
40:         address indexed account,
49:     modifier onlyOwner() { // <= FOUND
50:         require(_accessControl.isOwner(msg.sender), "onlyOwner");
51:         _;
52:     }
53: 
54:     modifier onlyAdmin() { // <= FOUND
55:         require(_accessControl.isAdmin(msg.sender), "onlyAdmin");

54:     modifier onlyAdmin() { // <= FOUND
55:         require(_accessControl.isAdmin(msg.sender), "onlyAdmin");
56:         _;
57:     }
58: 
59:     modifier onlyApprovedAdmin( // <= FOUND
60:         address account_

59:     modifier onlyApprovedAdmin( // <= FOUND
60:         address account_
61:     )
62:     {
63:         address admin = msg.sender;
64:         require(_accessControl.isAdmin(admin), "onlyAdmin");
65:         require(_isApprovedAdmin[account_][admin], "onlyApprovedAdmin");

[Low-6] Greater than comparisons made on state uints that can be set to max

Resolution

When state variables (uints) that can be set to their maximum value (type(uint256).max for example) are used in "greater than" comparisons, it introduces a risk of logic errors. If the state variable ever reaches this max value, any comparison expecting it to increment further will fail. This can halt or disrupt contract functionality. To avoid this, implement checks to ensure that the state variable doesn't exceed a certain threshold below the max value.

Num of instances: 3

Findings

Click to show findings

['83']

76:     function setSPercent(
77:         uint sPercent_
78:     )
79:         external
80:     {
81:         address account = msg.sender;
82:         LPercentage.validatePercent(sPercent_);
83:         require(sPercent_ >= _minSPercent, "sPercent_ invalid"); // <= FOUND
84:         SProfile storage profileData = _profileOf[account];
85:         profileData.nextSPercent = sPercent_;
86:         if (sPercent_ >  profileData.sPercent) {
87:             _updateSponsor(account, profileData.sponsor);
88:         }
89:         emit SetSPercent(account, sPercent_);
90:     }

['32']

26:     function _updateAthBalance(
27:         address account_,
28:         uint balance_
29:     )
30:         internal
31:     {
32:         if (balance_ > _athBalance && !isContract(account_)) { // <= FOUND
33:             _athBalance = balance_;
34:             emit UpdateAthBalance(account_, balance_);
35:         }
36:     }

['119']

112:     function setMinSPercentConfig(
113:         uint sPercent_
114:     )
115:         external
116:         onlyAdmin
117:     {
118:         LPercentage.validatePercent(sPercent_);
119:         _minSPercent = sPercent_; // <= FOUND
120:     }

[Low-7] Some tokens may revert when zero value transfers are made

Resolution

Reason: In Solidity, ERC20 token transfers of value 0 can sometimes lead to unexpected issues. This is particularly relevant when dealing with fractional token amounts that round to 0 when less than 1 of the smallest unit is transferred, leading to an effective transfer of nothing while still consuming gas. Furthermore, some ERC20 token implementations may revert on attempts to transfer a value of 0. However, note that this issue doesn't generally apply to wrapper native tokens like WETH.

Resolution: It's advisable to include a condition before any transfer operation to bypass the transaction if the transfer amount is 0. This saves unnecessary gas expenditure and prevents potential function reverts. For handling fractions, ensure token decimals are appropriately assigned and contemplate setting a minimum transfer threshold to avoid rounding down to 0. When dealing with wrapped tokens like WETH, special consideration should be given to their unique characteristics.

Num of instances: 4

Findings

Click to show findings

['60']

60:     function _cashOut(
61:         address to_,
62:         uint amount_
63:     )
64:         internal
65:     {
66:         _token.transfer(to_, amount_); // <= FOUND
67:         _updateBalance();
68:     }

['95']

95:     function clean()
96:         public
97:         virtual
98:     {
99:         require(_isCleanEnabled, "unable to clean");
100:         uint currentBal = currentBalance();
101:         if (currentBal > _lastestBalance) {
102:             uint amount = currentBal - _lastestBalance;
103:             _token.transfer(_cleanTo, amount); // <= FOUND
104:             emit Clean(amount);
105:         }
106:         _updateBalance();
107:     }

['25']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     {
34:         uint vestingA = amount_ * vestingPercent_ / 10000;
35:         uint directA = amount_ - vestingA;
36:         _token.transfer(invester_, directA); // <= FOUND
37:         _token.transfer(address(_vester), vestingA);
38:         _vester.lock(invester_, vestingDur_, cliff_);
39:     }

['63']

63:     function withdraw(
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     {
70:         _token.transfer(dest_, amount_); // <= FOUND
71:     }

[Low-8] Return values not checked for approve()

Resolution

The ERC-20 token standard does not dictate that the approve function must return a value. The function signature in the ERC-20 standard is function approve(address spender, uint tokens) public returns (bool success);. However, a well-implemented ERC-20 token contract will typically have approve return a boolean value indicating whether or not the operation was successful.

It's crucial to note that not all token contracts follow this practice. Some might not return a value, or they might return a value in a non-standard way. This inconsistency among token contracts is one reason why it's important to handle token interactions carefully in your smart contracts and to check the return value of approve when possible.

Num of instances: 4

Findings

Click to show findings

['208']

208:         weth.approve(address(router), wethA_); // <= FOUND

['244']

244:         wsteth.approve(address(router), wstethA_); // <= FOUND

['278']

278:         weth.approve(address(nonfungiblePositionManager), wethA_); // <= FOUND

['279']

279:         wsteth.approve(address(nonfungiblePositionManager), wstethA_); // <= FOUND

[Low-9] Token burning is centralized

Resolution

Centralized ERC20 token burning, where only a select few addresses have control over the burn function, can be viewed negatively. It introduces risks of manipulation, as the power to alter token supply is concentrated in the hands of a small group, potentially affecting token value unfairly. Such centralization can also introduce risks if the admin account(s) is compromised. Consider implementing a timelock feature so users can decide to opt out of the protocol if they do not agree with actions taken.

Num of instances: 1

Findings

Click to show findings

['165']

165:     function burn( // <= FOUND
166:         address account_,
167:         uint amount_
168:     )
169:         external
170:         virtual
171:         onlyAdmin
172:     {
173:         _burn(account_, amount_);
174:     }

[Low-10] Token minting is centralized

Resolution

Centralized ERC20 token minting, where only a select few addresses have control over the mint function, can be viewed negatively. It introduces risks of manipulation, as the power to alter token supply is concentrated in the hands of a small group, potentially affecting token value unfairly. Such centralization can also introduce risks if the admin account(s) is compromised. Consider implementing a timelock feature so users can decide to opt out of the protocol if they do not agree with actions taken.

Num of instances: 1

Findings

Click to show findings

['155']

155:     function mint( // <= FOUND
156:         address account_,
157:         uint amount_
158:     )
159:         external
160:         onlyAdmin
161:     {
162:         _mint(account_, amount_);
163:     }

[Low-11] Low level calls in solidity versions preceding 0.8.14 can result in an optimiser bug

Resolution

In Solidity versions 0.8.13 and 0.8.14, a known optimizer bug presents potential risks when a variable is used in a separate assembly block from the one in which it was stored. Specifically, the 'mstore' operation could be optimized out due to this bug, leading to the use of uninitialized memory. Although the current code does not exhibit this risky pattern of execution, it does utilize 'mstore' within assembly blocks, which introduces a vulnerability risk for future code modifications. As a preventative measure, it is advisable to avoid the usage of the afflicted Solidity versions, 0.8.13 and 0.8.14. Instead, consider utilizing a version that is not impacted by this optimizer bug to prevent potential memory initialization issues in your smart contract.

Num of instances: 2

Findings

Click to show findings

['136']

136:     assembly { // <= FOUND
137:         size := extcodesize(_addr)
138:     }

['57']

57:        assembly { // <= FOUND
58:             addr := create2(0, add(bytecode, 0x20), mload(bytecode), _salt)
59:             if iszero(extcodesize(addr)) {
60:                 revert(0, 0)
61:             }
62:         }

[Low-12] Empty receive functions can cause gas issues

Resolution

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.

Num of instances: 1

Findings

Click to show findings

['117']

117:     receive() external payable {}

[Low-13] Ownable2Step should be used in place of Ownable

Resolution

Ownable2Step further prevents risks posed by centralised privileges as there is a smaller likelihood of the owner being wrongfully changed

Num of instances: 1

Findings

Click to show findings

['8']

8: contract AccessControl is Ownable  // <= FOUND

[Low-14] The call send() should almost never be used as instead of throwing an error it returns false. Use transfer( instead

Resolution

In Solidity, the use of send() for transferring Ether is generally discouraged because it doesn't throw an error on failure, but rather returns false. This can lead to security vulnerabilities if the return value is not properly handled. Instead, it's recommended to use transfer(), which automatically reverts the transaction if the transfer fails. This behavior ensures that the entire transaction, including all state changes, is reverted, thus preventing partial execution of functions that might lead to inconsistencies or vulnerabilities in the contract's state. transfer() provides a safer alternative by enforcing error handling at the EVM level, ensuring more predictable and secure contract behavior.

Num of instances: 1

Findings

Click to show findings

['275']

275:             to.send(address(this).balance); // <= FOUND

[Low-15] Upgradable contracts should have a __gap variable

Resolution

This is to ensure the team can add new state variables without compromising compatability.

Num of instances: 4

Findings

Click to show findings

['9']

9: contract AdaptiveDistributor is Cashier, Initializable 

['14']

14: contract Distributor is Cashier, Initializable, UseAccessControl 

['6']

6: contract Initializable 

['38']

38: contract UseAccessControl is Initializable 

[Low-16] No limits when setting min/max amounts

Resolution

When settings min/max state variables, ensure there a require checks in place to prevent incorrect values from being set

Num of instances: 1

Findings

Click to show findings

['63']

63:     function updateMaxEarning(
64:         address account_,
65:         uint maxEarning_ // <= FOUND
66:     )
67:         external
68:         onlyAdmin
69:     {
70:         _updateMaxEarning(account_, maxEarning_); // <= FOUND
71:     }

[Low-17] Initializer function can be front run

Resolution

In Solidity contract deployment, not making the initialize() function call atomic with the contract creation can leave a vulnerability window. A malicious actor could exploit this time gap and call initialize() before the intended initialization. This action could disrupt the contract's setup, potentially necessitating a full contract re-deployment to ensure proper initialization. To mitigate such risks, it's advised to use a factory contract. This factory contract can be programmed to deploy and initialize a new contract in a single atomic transaction, closing the window of vulnerability and ensuring correct and secure contract initialization.

Num of instances: 14

Findings

Click to show findings

[]

40:     function init(
41:         address dToken_,
42:         address rewardToken_
43:     )
44:         public
45:         initializer
46:     

[]

108:     function initController(
109:         address[] memory contracts_
110:     )
111:         external
112:         initializer
113:     

[]

27:     function initDCT(
28:         address accessControl_,
29:         address rewardPool_,
30:         address premineAddress_,
31:         uint256 premineAmount_,
32:         address cleanTo_
33:     )
34:         external
35:         initializer
36:     

[]

14:     function initDToken(
15:         address accessControl_,
16:         bool inPrivateMode_,
17:         string memory name_,
18:         string memory symbol_,
19:         address[] memory distributorAddrs_
20:     )
21:         public
22:         initializer
23:     

[]

34:     function initDistributor(
35:         address accessControl_,
36:         address dToken_,
37:         address rewardToken_
38:     )
39:         public
40:         initializer
41:     

[]

35:     function initEarning(
36:         address token_,
37:         address profileCAddr_,
38:         address accessControl_,
39:         string memory name_,
40:         string memory symbol_,
41:         address cleanTo_
42:     )
43:         public
44:         virtual
45:         initializer
46:     

[]

39:     function initEthSharing(
40:         address accessControl_,
41:         uint devTeamPercent_,
42:         uint defaultOwnerPercent_,
43:         uint defaultUserPercent_,
44:         bool inDefaultOnlyMode_
45: 
46:     )
47:         external
48:         initializer
49:     

[]

42:     function initLocker(
43:         address accessControl_,
44:         address token_,
45:         address profileCAddr_,
46:         address penaltyAddress_,
47:         address cleanTo_
48:     )
49:         public
50:         initializer
51:     

[]

58:     function initPERC20(
59:         address accessControl_,
60:         bool inPrivateMode_,
61:         string memory name_,
62:         string memory symbol_
63:     )
64:         public
65:         virtual
66:         initializer
67:     

[]

35:     function initPoolFactory(
36:         address accessControl_,
37:         address geth_,
38:         address dct_
39:     )
40:         external
41:         initializer
42:     

[]

40:     function initProfile(
41:         address accessControl_
42:     )
43:         external
44:         initializer
45:     

[]

73:     function initUseAccessControl(
74:         address accessControl_
75:     )
76:         public
77:         initializer
78:     

[]

23:     function initVester(
24:         address accessControl_,
25:         address token_,
26:         address cleanTo_
27:     )
28:         public
29:         initializer
30:     

[]

119:     function initVoting(
120:         address accessControl_,
121:         address votingTokenAddr_,
122:         address gethAddr_,
123:         address eEarningAddr_,
124:         address cleanTo_
125:     )
126:         external
127:         initializer
128:     

[Low-18] Use _disableInitializers() to ensure initialization occurs once

Resolution

disableInitializers() should be used in upgradeable contracts to ensure the initializer functions can't be called more than once. In upgradeable contracts, initializer functions set initial state and values, but if they can be invoked multiple times, it could lead to unexpected behavior or vulnerabilities. By calling disableInitializers() after the initial setup, you essentially lock the initializer functions, ensuring they can only be called once during the contract's lifecycle. This prevents repeated initializations, helping to maintain the integrity and security of the contract, and providing a safeguard against potential manipulation or misuse of the initialization functions.

Num of instances: 4

Findings

Click to show findings

['9']

9: contract AdaptiveDistributor is Cashier, Initializable 

['14']

14: contract Distributor is Cashier, Initializable, UseAccessControl 

['6']

6: contract Initializable 

['38']

38: contract UseAccessControl is Initializable 

[Low-19] Minting to the zero address should be avoided

Resolution

Minting tokens to the zero address in Solidity is a potential pitfall that should be carefully guarded against. The zero address (0x0) is generally used as a default value and represents an uninitialized or null address. Minting tokens to this address effectively burns them, making them inaccessible and permanently removing them from the total supply. This could lead to unintended token loss if performed accidentally. To prevent this, it's important to include a check in the minting function to ensure that the target address is not the zero address. Using a library like OpenZeppelin's Address can provide utility functions like requireNonZero, which simplifies this check and enhances the security of the minting function.

Num of instances: 1

Findings

Click to show findings

['155']

155:     function mint(
156:         address account_,
157:         uint amount_
158:     )
159:         external
160:         onlyAdmin
161:     {
162:         _mint(account_, amount_); // <= FOUND
163:     }

[Low-20] Loss of precision

Resolution

Dividing by large numbers in Solidity can cause a loss of precision due to the language's inherent integer division behavior. Solidity does not support floating-point arithmetic, and as a result, division between integers yields an integer result, truncating any fractional part. When dividing by a large number, the resulting value may become significantly smaller, leading to a loss of precision, as the fractional part is discarded.

Num of instances: 16

Findings

Click to show findings

['110']

110:     function claimableOf(
111:         address holder_
112:     )
113:         public
114:         view
115:         returns(uint reward)
116:     {
117:         uint bal = _dToken.balanceOf(holder_);
118:         SHolder memory holder = holders[holder_];
119:         uint deltaRpt = rpt - holder.lastRpt;
120:         if (holder.regBal > bal) {
121:             reward = deltaRpt * bal / MFACTOR; // <= FOUND
122:         } else {
123:             reward = deltaRpt * holder.regBal / MFACTOR;
124:         }
125:     }

['198']

198:     function _updateSponsor(
199:         address payable poolOwner_,
200:         address staker_,
201:         uint minSPercent_
202:     )
203:         internal
204:     {
205:         if (poolOwner_ == staker_) {
206:             return;
207:         }
208:         IProfile.SProfile memory profile = _profileC.profileOf(poolOwner_);
209:         if (profile.sponsor == staker_) {
210:             return;
211:         }
212:         require(profile.nextSPercent >= minSPercent_, "profile rate changed");
213:         IPoolFactory.SPool memory pool = _poolFactory.getPool(poolOwner_);
214:         IDToken p2UDtoken = IDToken(pool.dToken);
215:         uint timeDiff = block.timestamp - profile.updatedAt;
216:         if (timeDiff > _maxSponsorAfter) {
217:             timeDiff = _maxSponsorAfter;
218:         }
219: 
220:         uint sponsorDTokenBalance = p2UDtoken.balanceOf(profile.sponsor);
221:         uint stakerDTokenBalance = p2UDtoken.balanceOf(staker_);
222:         uint sponsorBonus = sponsorDTokenBalance * (_maxSponsorAdv - 1) // <= FOUND
223:             * timeDiff / _maxSponsorAfter;
224:         uint sponsorPower = sponsorDTokenBalance + sponsorBonus;
225:         if (stakerDTokenBalance > sponsorPower || poolOwner_ == profile.sponsor) {
226:             address[] memory pools = new address[](1);
227:             pools[0] = poolOwner_;
228:             earningPulls(poolOwner_, pools, poolOwner_);
229:             _profileC.updateSponsor(poolOwner_, staker_);
230:         }
231:     }

['478']

478:     function earningPulls(
479:         address account_,
480:         address[] memory poolOwners_,
481:         address bountyPullerTo_
482:     )
483:         public
484:         tryPublicMint
485:     {
486:         _dEarning.clean();
487:         _eEarning.clean();
488: 
489:         
490:         _dP2PDistributor.distribute();
491: 
492:         
493:         
494: 
495:         
496:         
497: 
498:         _distributorClaimFor(address(_eDP2PDistributor), account_, address(this));
499:         _distributorClaimFor(address(_dDP2PDistributor), account_, address(_dEarning));
500: 
501:         for(uint i = 0; i < poolOwners_.length; i++) {
502:             _earningPull(account_, poolOwners_[i]);
503:         }
504: 
505:         if (bountyPullerTo_ == account_) {
506:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
507:         } else {
508:             uint256 amountForPuller = _geth.balanceOf(address(this)) * _bountyPullEarningPercent / LPercentage.DEMI; // <= FOUND
509: 
510:             LLido.sellWsteth(amountForPuller);
511:             LLido.wethToEth();
512:             payable(bountyPullerTo_).transfer(address(this).balance);
513: 
514:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
515:         }
516: 
517:         _dEarning.update(account_, true);
518:         _eEarning.update(account_, true);
519: 
520:         _reCalFs(account_);
521:     }

['589']

589:     function earningReinvest(
590:         bool isEth_,
591:         address payable poolOwner_,
592:         uint duration_,
593:         uint amount_,
594:         
595:         uint minSPercent_,
596:         uint poolConfigCode_
597:     )
598:         external
599:     {
600:         address account = msg.sender;
601:         if (isEth_) {
602:             LLocker.SLock memory oldLockData = _eLocker.getLockData(account, poolOwner_);
603:             uint realDuration = duration_ + LLocker.restDuration(oldLockData);
604:             if (realDuration > _maxDuration) {
605:                 realDuration = _maxDuration;
606:             }
607:             uint maxEarning = _eEarning.maxEarningOf(account);
608:             maxEarning -= amount_ * realDuration / _maxDuration; // <= FOUND
609:             _eEarning.updateMaxEarning(account, maxEarning);
610:         }
611:          earningWithdraw(isEth_, amount_, payable(address(this)), 0);
612:         _stake(isEth_, account, poolOwner_, duration_, minSPercent_, poolConfigCode_);
613:     }

['114']

114:     function calReward(
115:         uint ptr_,
116:         uint dTokenBalance_,
117:         int256 adjustedReward_
118:     )
119:         public
120:         pure
121:         returns(uint)
122:     {
123:         int256 mulReward = int256(dTokenBalance_ * ptr_) - adjustedReward_;
124:         require(mulReward >= 0, "rewardOf,something gone wrong!");
125:         return uint(mulReward) / MFACTOR; // <= FOUND
126:     }

['11']

11:     function calEP2PDBalance(
12:         uint fs_,
13:         uint booster_,
14:         uint totalP2UBalance_
15:     )
16:         internal
17:         pure
18:         returns(uint)
19:     {
20:        return fs_ * booster_ * totalP2UBalance_ / LPercentage.DEMIE2; // <= FOUND
21:     }

['49']

49:     function calBurnStakingPower(
50:         uint powerBalance_,
51:         uint unlockedA_,
52:         uint totalLockedA_
53:     )
54:         internal
55:         pure
56:         returns(uint)
57:     {
58:         return powerBalance_ * unlockedA_ / totalLockedA_; // <= FOUND
59:     }

['79']

79:     function calMultiplierForOldAmount(
80:         uint lockTime_
81:     )
82:         internal
83:         pure
84:         returns(uint)
85:     {
86:         uint x = lockTime_ * LPercentage.DEMI / 30 days;
87:         uint rs = (1300 * x / LPercentage.DEMI); // <= FOUND
88:         return rs;
89:     }

['91']

91:     function calMultiplier(
92:         uint lockTime_
93:     )
94:         internal
95:         pure
96:         returns(uint)
97:     {
98:         uint x = lockTime_ * LPercentage.DEMI / 30 days;
99:         uint rs = (1300 * x / LPercentage.DEMI) + 8800; // <= FOUND
100:         return rs;
101:     }

['103']

103:     function calAQuorum(
104:         uint aEthValue_,
105:         uint dEthValue_,
106:         uint voterPercent_,
107:         uint freezeDuration_,
108:         uint freezeDurationUnit_
109:     )
110:         internal
111:         pure
112:         returns(uint)
113:     {
114:         uint tmp = LPercentage.DEMI - voterPercent_;
115:         uint leverage = LPercentage.DEMIE2 * // <= FOUND
116:             dEthValue_ * freezeDuration_ / aEthValue_ / freezeDurationUnit_ / tmp;
117:         if (leverage < LPercentage.DEMI) {
118:             leverage = LPercentage.DEMI;
119:         }
120:         return LPercentage.DEMI * leverage / (leverage + LPercentage.DEMI);
121:     }

['270']

270:     function mintNewPosition(
271:         uint wethA_,
272:         uint wstethA_
273:     )
274:         internal
275:         returns
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1)
277:     {
278:         weth.approve(address(nonfungiblePositionManager), wethA_);
279:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
280: 
281:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth);
282:         address token1 = token0 == address(weth) ? address(wsteth) : address(weth);
283:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_;
284:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_;
285: 
286:         INonfungiblePositionManager.MintParams
287:             memory params = INonfungiblePositionManager.MintParams({
288:                 token0: token0, // <= FOUND
289:                 token1: token1,
290:                 fee: POOL_FEE,
291:                 tickLower: (MIN_TICK / TICK_SPACING) * TICK_SPACING,
292:                 tickUpper: (MAX_TICK / TICK_SPACING) * TICK_SPACING,
293:                 amount0Desired: amount0ToAdd,
294:                 amount1Desired: amount1ToAdd,
295:                 amount0Min: 0,
296:                 amount1Min: 0,
297:                 recipient: address(this),
298:                 deadline: block.timestamp
299:             });
300: 
301:         (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(
302:             params
303:         );
304:     }

['68']

68:     function isUnlocked(
69:         SLock memory lockData_,
70:         uint fs_,
71:         bool isPoolOwner_
72:     )
73:         internal
74:         view
75:         returns(bool)
76:     {
77:         uint mFactor = isPoolOwner_ ? 2 * LPercentage.DEMI - fs_ : fs_;
78:         uint duration = lockData_.duration * mFactor / LPercentage.DEMI; // <= FOUND
79:         uint elapsedTime = block.timestamp - lockData_.startedAt;
80:         return elapsedTime >= duration;
81:     }

['83']

83:     function calDuration(
84:         SLock memory lockData_,
85:         uint fs_,
86:         bool isPoolOwner_
87:     )
88:         internal
89:         pure
90:         returns(uint)
91:     {
92:         uint mFactor = isPoolOwner_ ? 2 * LPercentage.DEMI - fs_ : fs_;
93:         uint duration = lockData_.duration * mFactor / LPercentage.DEMI; // <= FOUND
94:         return duration;
95:     }

['20']

20:     function getPercentA(
21:         uint value,
22:         uint percent
23:     )
24:         internal
25:         pure
26:         returns(uint)
27:     {
28:         return value * percent / DEMI; // <= FOUND
29:     }

['87']

87:     function _withdraw(
88:         address account_,
89:         address poolOwner_,
90:         address dest_,
91:         uint amount_,
92:         bool isForced_
93:     )
94:         internal
95:     {
96:         bytes32 lockId = LLocker.getLockId(account_, poolOwner_);
97:         LLocker.SLock storage lockData = _lockData[lockId];
98:         bool isPoolOwner = account_ == poolOwner_;
99:         uint fs = _profileC.fsOf(poolOwner_);
100:         if (!isForced_) {
101:             require(LLocker.isUnlocked(lockData, fs, isPoolOwner), "not unlocked");
102:         }
103:         uint duration = LLocker.calDuration(lockData, fs, isPoolOwner);
104:         uint pastTime = block.timestamp - lockData.startedAt;
105:         if (pastTime > duration) {
106:             pastTime = duration;
107:         }
108: 
109:         lockData.amount -= amount_;
110:         uint total = amount_;
111:         uint receivedA = total * pastTime / duration; // <= FOUND
112:         _cashOut(dest_, receivedA);
113:         if (total != receivedA) {
114:             _cashOut(_penaltyAddress, total - receivedA);
115:         }
116: 
117:         emit Withdraw(account_, poolOwner_, dest_, amount_);
118:         emit UpdateLockData(account_, poolOwner_, _lockData[lockId]);
119:     }

['109']

109:     function currentLockData(
110:         address account_
111:     )
112:         public
113:         view
114:         returns(uint restA, uint restDuration)
115:     {
116:         LLocker.SLock memory lockData = _lockData[account_];
117:         restDuration = LLocker.restDuration(lockData);
118:         restA = restDuration >= lockData.duration // <= FOUND
119:             ? lockData.amount
120:             : lockData.amount * restDuration / lockData.duration;
121:     }

[Low-21] Use of onlyOwner functions can be lost

Resolution

In Solidity, renouncing ownership of a contract essentially transfers ownership to the zero address. This is an irreversible operation and has considerable security implications. If the renounceOwnership function is used, the contract will lose the ability to perform any operations that are limited to the owner. This can be problematic if there are any bugs, flaws, or unexpected events that require owner intervention to resolve. Therefore, in some instances, it is better to disable or omit the renounceOwnership function, and instead implement a secure transferOwnership function. This way, if necessary, ownership can be transferred to a new, trusted party without losing the potential for administrative intervention.

Num of instances: 1

Findings

Click to show findings

['8']

8: contract AccessControl is Ownable  // <= FOUND

[Low-22] Constant decimal values

Resolution

The use of fixed decimal values such as 1e18 or 1e8 in Solidity contracts can lead to inaccuracies, bugs, and vulnerabilities, particularly when interacting with tokens having different decimal configurations. Not all ERC20 tokens follow the standard 18 decimal places, and assumptions about decimal places can lead to miscalculations.

Resolution: Always retrieve and use the decimals() function from the token contract itself when performing calculations involving token amounts. This ensures that your contract correctly handles tokens with any number of decimal places, mitigating the risk of numerical errors or under/overflows that could jeopardize contract integrity and user funds.

Num of instances: 3

Findings

Click to show findings

['38']

38:     uint constant private MFACTOR = 1e18; // <= FOUND

['80']

80:     uint public _minStakeETHAmount = 0.001e18; // <= FOUND

['93']

93:     uint public _minDefenderFund = 0.001e18; // <= FOUND

[Low-23] Revert on Transfer to the Zero Address

Resolution

Many ERC-20 and ERC-721 token contracts implement a safeguard that reverts transactions which attempt to transfer tokens to the zero address. This is because such transfers are often the result of programming errors. The OpenZeppelin library, a popular choice for implementing these standards, includes this safeguard. For token contract developers who want to avoid unintentional transfers to the zero address, it's good practice to include a condition that reverts the transaction if the recipient's address is the zero address.

Num of instances: 6

Findings

Click to show findings

['141']

141:     function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { // <= FOUND
142:         require(!_inPrivateMode, "_inPrivateMode");
143:         return super.transferFrom(from, to , amount); // <= FOUND
144:     }

['60']

60:     function _cashOut(
61:         address to_,
62:         uint amount_
63:     )
64:         internal
65:     {
66:         _token.transfer(to_, amount_); // <= FOUND
67:         _updateBalance();
68:     }

['95']

95:     function clean()
96:         public
97:         virtual
98:     {
99:         require(_isCleanEnabled, "unable to clean");
100:         uint currentBal = currentBalance();
101:         if (currentBal > _lastestBalance) {
102:             uint amount = currentBal - _lastestBalance;
103:             _token.transfer(_cleanTo, amount); // <= FOUND
104:             emit Clean(amount);
105:         }
106:         _updateBalance();
107:     }

['136']

136:     function transfer(address to, uint256 amount) public virtual override returns (bool) {
137:         require(!_inPrivateMode, "_inPrivateMode");
138:         return super.transfer(to, amount); // <= FOUND
139:     }

['25']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     {
34:         uint vestingA = amount_ * vestingPercent_ / 10000;
35:         uint directA = amount_ - vestingA;
36:         _token.transfer(invester_, directA); // <= FOUND
37:         _token.transfer(address(_vester), vestingA); // <= FOUND
38:         _vester.lock(invester_, vestingDur_, cliff_);
39:     }

['63']

63:     function withdraw(
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     {
70:         _token.transfer(dest_, amount_); // <= FOUND
71:     }

[Low-24] Critical functions should have a timelock

Resolution

Critical functions, especially those affecting protocol parameters or user funds, are potential points of failure or exploitation. To mitigate risks, incorporating a timelock on such functions can be beneficial. A timelock requires a waiting period between the time an action is initiated and when it's executed, giving stakeholders time to react, potentially vetoing malicious or erroneous changes. To implement, integrate a smart contract like OpenZeppelin's TimelockController or build a custom mechanism. This ensures governance decisions or administrative changes are transparent and allows for community or multi-signature interventions, enhancing protocol security and trustworthiness.

Num of instances: 3

Findings

Click to show findings

['146']

146:     function setInPrivateMode( // <= FOUND
147:         bool inPrivateMode_
148:     )
149:         external
150:         onlyOwner
151:     

['102']

102:     function setDefaultSPercentConfig( // <= FOUND
103:         uint sPercent_
104:     )
105:         external
106:         onlyAdmin
107:     

['112']

112:     function setMinSPercentConfig( // <= FOUND
113:         uint sPercent_
114:     )
115:         external
116:         onlyAdmin
117:     

[Low-25] Use of abi.encodePacked with dynamic types inside keccak256

Resolution

Using abi.encodePacked with dynamic types for hashing functions like keccak256 can be risky due to the potential for hash collisions. This function concatenates arguments tightly, without padding, which might lead to different inputs producing the same hash. This is especially problematic with dynamic types, where the boundaries between inputs can blur. To mitigate this, use abi.encode instead. abi.encode pads its arguments to 32 bytes, creating clear distinctions between different inputs and significantly reducing the chance of hash collisions. This approach ensures more reliable and collision-resistant hashing, crucial for maintaining data integrity and security in smart contracts.

Num of instances: 1

Findings

Click to show findings

['22']

22:         return keccak256(abi.encodePacked(account_, poolOwner_)); // <= FOUND

[Low-26] Inconsistent expiry logic using block global values

Resolution

Inconsistent usage of block global values in expiry logic, marked by a mix of '<' and '<=' comparisons, can lead to unpredictable and unreliable contract behavior. This inconsistency may arise in conditions where block number or timestamp is used to determine the expiration of a transaction or a contract state. Using both '<' and '<=' for similar checks can create ambiguity and errors in contract execution, potentially leading to unexpected outcomes. To ensure reliability and clarity, it's essential to standardize the comparison operators in expiry logic. Consistently using either '<' or '<=' across the contract helps maintain predictable behavior and avoids potential off-by-one errors, enhancing the contract's overall robustness and user trust.

Num of instances: 1

Findings

Click to show findings

['17']

17: contract Voting is UseAccessControl, Cashier {
18: 
19:     modifier onlyInVotingTime(
20:         uint voteId_
21:     )
22:     {
23:         require(voteId_ < _totalVotes, "invalid voteId");
24:         SVote storage vote = _votes[voteId_];
25:         require(vote.startedAt <= block.timestamp, "voting not started"); // <= FOUND
26:         require(vote.endAt >= block.timestamp, "voting already ended");
27:         _;
28:     }
29: 
30:     struct SVote{
31:         address attacker;
32:         address defender;
33: 
34:         uint aEthValue;
35:         uint dEthValue;
36: 
37:         uint voterPercent;
38:         uint aQuorum;
39: 
40:         uint startedAt;
41:         uint endAt;
42: 
43:         uint attackerPower;
44:         uint defenderPower;
45:         mapping(address => uint) powerOf;
46:         mapping(address => bool) isForAttacker;
47: 
48:         uint totalClaimed;
49:         mapping(address => bool) isClaimed;
50: 
51:         bool isFinalized;
52:         bool isAttackerWon;
53:         uint winVal;
54:         uint winnerPower;
55:         bool isClosed;
56:     }
57: 
58:     event CreateVote(
59:         uint indexed voteId,
60: 
61:         address indexed attacker,
62:         address indexed defender,
63: 
64:         uint aEthValue,
65:         uint dEthValue,
66: 
67:         uint voterPercent,
68:         uint aQuorum,
69: 
70:         uint startedAt,
71:         uint endAt
72:     );
73: 
74:     event RemoveVoter(
75:         uint indexed voteId,
76:         address indexed voter,
77:         bool indexed isForAttacker,
78:         uint power
79:     );
80: 
81:     event AddVoter(
82:         uint indexed voteId,
83:         address indexed voter,
84:         bool indexed isForAttacker,
85:         uint power
86:     );
87: 
88:     event CloseVote(
89:         uint indexed voteId,
90:         uint cleanedVal,
91:         address dest
92:     );
93: 
94:     event Finalize(
95:         uint indexed voteId,
96:         bool isAttackerWon,
97:         uint winnerPower,
98:         uint winVal
99:     );
100: 
101:     event ClaimFor(
102:         uint indexed voteId,
103:         address indexed voter,
104:         uint winVal
105:     );
106: 
107:     mapping(uint => SVote) private _votes;
108:     uint private _totalVotes;
109: 
110:     IERC20 private _votingToken;
111:     IERC20  private _geth;
112: 
113:     IEarning private _eEarning;
114: 
115:     mapping(address => uint) private _defenderEarningFreezedOf;
116: 
117:     receive() external payable {}
118: 
119:     function initVoting(
120:         address accessControl_,
121:         address votingTokenAddr_,
122:         address gethAddr_,
123:         address eEarningAddr_,
124:         address cleanTo_
125:     )
126:         external
127:         initializer
128:     {
129:         initUseAccessControl(accessControl_);
130:         _votingToken = IERC20(votingTokenAddr_);
131:         _geth = IERC20(gethAddr_);
132:         _initCashier(gethAddr_, cleanTo_);
133:         _eEarning = IEarning(eEarningAddr_);
134:     }
135: 
136:     function createVote(
137:         address attacker_,
138:         address defender_,
139: 
140:         uint aEthValue_,
141:         uint dEthValue_,
142: 
143:         uint voterPercent_,
144:         uint aQuorum_,
145: 
146:         uint startedAt_,
147:         uint endAt_
148:     )
149:         external
150:         onlyAdmin
151:     {
152:         uint voteId = _totalVotes;
153:         _totalVotes++;
154:         SVote storage vote = _votes[voteId];
155:         vote.attacker = attacker_;
156:         vote.defender = defender_;
157: 
158:         vote.aEthValue = aEthValue_;
159:         vote.dEthValue = dEthValue_;
160: 
161:         _defenderEarningFreezedOf[defender_] += dEthValue_;
162: 
163:         LPercentage.validatePercent(voterPercent_);
164:         LPercentage.validatePercent(aQuorum_);
165:         vote.voterPercent = voterPercent_;
166:         vote.aQuorum = aQuorum_;
167:         uint inVal = _cashIn();
168:         require(aEthValue_ + dEthValue_ == inVal, "eth value incorrect");
169: 
170:         vote.startedAt = startedAt_;
171:         require(startedAt_ >= block.timestamp, "must start in future");
172:         vote.endAt = endAt_;
173:         require(endAt_ >= startedAt_, "duration not negative");
174:         require(endAt_ < block.timestamp + 365 days, "duration too long"); // <= FOUND
175:         emit CreateVote(
176:             voteId,
177:             attacker_,
178:             defender_,
179:             aEthValue_,
180:             dEthValue_,
181:             voterPercent_,
182:             aQuorum_,
183:             startedAt_,
184:             endAt_
185:         );
186: 
187:         _addVoter(voteId, attacker_, true);
188:         _addVoter(voteId, defender_, false);
189:     }
190: 
191:     function _removeVoter(
192:         uint voteId_,
193:         address voter_
194:     )
195:         internal
196:     {
197:         SVote storage vote = _votes[voteId_];
198:         uint power = vote.powerOf[voter_];
199:         if (power > 0) {
200:             if (vote.isForAttacker[voter_]) {
201:                 vote.attackerPower -= power;
202:             } else {
203:                 vote.defenderPower -= power;
204:             }
205:             vote.powerOf[voter_] = 0;
206:             emit RemoveVoter(voteId_, voter_, vote.isForAttacker[voter_], power);
207:         }
208:     }
209: 
210:     function _addVoter(
211:         uint voteId_,
212:         address voter_,
213:         bool isForAttacker_
214:     )
215:         internal
216:     {
217:         _removeVoter(voteId_, voter_);
218: 
219:         SVote storage vote = _votes[voteId_];
220:         uint power = _votingToken.balanceOf(voter_);
221: 
222:         uint voteDuration = vote.endAt - vote.startedAt;
223:         uint pastTime = block.timestamp - vote.startedAt;
224:         uint restDuration = voteDuration - pastTime;
225:         power = power * restDuration / voteDuration;
226: 
227:         if (power == 0) {
228:             
229:             power = 1;
230:         }
231: 
232:         if (isForAttacker_) {
233:             vote.attackerPower += power;
234:         } else {
235:             vote.defenderPower += power;
236:         }
237:         vote.powerOf[voter_] = power;
238:         vote.isForAttacker[voter_] = isForAttacker_;
239:         emit AddVoter(voteId_, voter_, isForAttacker_, power);
240:     }
241: 
242:     function updatePower(
243:         uint voteId_,
244:         bool isForAttacker_
245:     )
246:         external
247:         onlyInVotingTime(voteId_)
248:     {
249:         address voter = msg.sender;
250:         _addVoter(voteId_, voter, isForAttacker_);
251:     }
252: 
253:     function _tryFinalize(
254:         uint voteId_
255:     )
256:         internal
257:     {
258:         SVote storage vote = _votes[voteId_];
259:         require(!vote.isFinalized, "already finalized");
260:         require(vote.endAt < block.timestamp, "vote not ended"); // <= FOUND
261: 
262:         vote.isFinalized = true;
263:         uint totalPower = vote.attackerPower + vote.defenderPower;
264:         uint reqPower = LPercentage.getPercentA(totalPower, vote.aQuorum);
265:         vote.isAttackerWon = vote.attackerPower > reqPower;
266: 
267:         if (vote.isAttackerWon) {
268:             vote.winVal = LPercentage.getPercentA(vote.dEthValue, vote.voterPercent);
269:             vote.winnerPower = vote.attackerPower;
270:             uint toWinnerVal = vote.aEthValue + vote.dEthValue - vote.winVal;
271:             address payable to = payable(vote.attacker);
272:             
273:             LLido.sellWsteth(toWinnerVal);
274:             LLido.wethToEth();
275:             to.send(address(this).balance);
276:             clean();
277:         } else {
278:             vote.winVal = LPercentage.getPercentA(vote.aEthValue, vote.voterPercent);
279:             vote.winnerPower = vote.defenderPower;
280:             uint unfreezeVal = vote.dEthValue;
281:             uint rewardWinnerVal = vote.aEthValue - vote.winVal;
282:             address payable to = payable(vote.defender);
283: 
284:             _eEarning.clean();
285:             
286:             _cashOut(address(_eEarning), unfreezeVal);
287:             _eEarning.update(to, false);
288:             
289:             _cashOut(address(_eEarning), rewardWinnerVal);
290:             _eEarning.update(to, true);
291:         }
292:         _defenderEarningFreezedOf[vote.defender] -= vote.dEthValue;
293: 
294:         emit Finalize(voteId_, vote.isAttackerWon, vote.winnerPower, vote.winVal);
295:     }
296: 
297:     function claimFor(
298:         uint voteId_,
299:         address voter_
300:     )
301:         external
302:         onlyAdmin
303:     {
304:         SVote storage vote = _votes[voteId_];
305:         require(!vote.isClosed, "already closed");
306:         if (!vote.isFinalized) {
307:             _tryFinalize(voteId_);
308:         }
309: 

[Low-27] Vulnerable version of openzeppelin contracts used

Resolution

OpenZeppelin versions of 4.9.2 and below are vulnerable to exploits, please consider upgrading to 4.9.3 or above. See: https://security.snyk.io/package/npm/@openzeppelin%2Fcontracts for more details

Num of instances: 1

Findings

Click to show findings

['[1](project_package.txt: 1-32)']

1: {
2:   "name": "hardhat-project",
3:   "devDependencies": {
4:     "@ethersproject/abi": "^5.7.0",
5:     "@ethersproject/providers": "^5.7.2",
6:     "@nomicfoundation/hardhat-chai-matchers": "^1.0.6",
7:     "@nomicfoundation/hardhat-network-helpers": "^1.0.8",
8:     "@nomicfoundation/hardhat-toolbox": "^2.0.2",
9:     "@nomiclabs/hardhat-ethers": "^2.2.2",
10:     "@nomiclabs/hardhat-etherscan": "^3.1.7",
11:     "@typechain/ethers-v5": "^10.2.0",
12:     "@typechain/hardhat": "^6.1.5",
13:     "@types/chai": "^4.3.4",
14:     "@types/mocha": "^10.0.1",
15:     "@types/node": "^18.15.5",
16:     "chai": "^4.3.7",
17:     "ethers": "^5.7.2",
18:     "hardhat": "^2.13.0",
19:     "hardhat-contract-sizer": "^2.10.0",
20:     "hardhat-gas-reporter": "^1.0.9",
21:     "solidity-coverage": "^0.8.2",
22:     "ts-node": "^10.9.1",
23:     "typechain": "^8.1.1",
24:     "typescript": "^5.0.2"
25:   },
26:   "scripts": {
27:     "deploy": "env-cmd -f .env npx hardhat run scripts/deploy.ts --network deploy",
28:     "compile": "npx hardhat compile",
29:     "start-node": "npx hardhat node"
30:   },
31:   "dependencies": {
32:     "@openzeppelin/contracts": "4.9.0", // <= FOUND
33:     "@uniswap/v2-core": "^1.0.1",
34:     "canonical-weth": "^1.4.0",
35:     "dotenv": "^16.0.3",
36:     "env-cmd": "^10.1.0"
37:   }
38: }
39: 

[Low-28] Division in comparison

Resolution

To ensure accuracy in comparisons within programming, especially when dealing with integers, it's often more efficient to use multiplication rather than division. This approach stems from the fact that division operations are generally slower and more complex than multiplication. And in the context of solidity they can cause precision errors.

Suppose you want to compare if a/b is greater than c/d (where a, b, c, and d are integers). Instead of performing division, which is prone to precision errors, you can cross-multiply to avoid division. The comparison a/b > c/d is equivalent to ad > bc. This way, you only use multiplication, which is faster and avoids potential inaccuracies or complexities associated with division.

Num of instances: 1

Findings

Click to show findings

['663']

663:         require(aEthValue <= dEthValue_ && aEthValue * LPercentage.DEMI / dEthValue_ >= _minAttackerFundRate, "aEthValue invalid"); // <= FOUND

[Low-29] Contract contains payable functions but no withdraw/sweep function

Resolution

In smart contract development, particularly for Ethereum, having payable functions without a corresponding withdraw or sweep function can lead to potential issues. Payable functions allow the contract to receive Ether, but without a mechanism to withdraw these funds, the Ether can become locked within the contract indefinitely. This situation might be intentional in some cases (like a burn function), but generally, it’s a design oversight. A withdraw or sweep function is necessary to transfer Ether out of the contract to a specific address, typically the owner's or a designated recipient. Without this, the contract lacks flexibility in managing its funds, potentially leading to lost or inaccessible Ether.

Num of instances: 2

Findings

Click to show findings

['24']

24: contract Controller is UseAccessControl 

['17']

17: contract Voting is UseAccessControl, Cashier 

[Low-30] Consider the case where totalSupply is zero

Resolution

In smart contracts, especially those dealing with tokens or financial calculations, functions that perform operations based on totalSupply must account for the scenario where totalSupply is zero. Neglecting this edge case can lead to division by zero errors, causing transactions to revert and potentially disrupting contract functionality. This is critical in functions calculating percentages, rewards, or distributions. Proper checks should be implemented to ensure totalSupply is greater than zero before performing division or similar operations. Handling this case ensures the contract remains robust, preventing logical errors and maintaining the integrity of its operations.

Num of instances: 2

Findings

Click to show findings

['161']

147:     function _reCalEP2PDBalance(
148:         address poolOwner_
149:     )
150:         internal
151:     {
152:         if (_poolFactory.isCreated(poolOwner_)) {
153:             IPoolFactory.SPool memory pool = _poolFactory.getPool(poolOwner_);
154:             IDToken p2UDtoken = IDToken(pool.dToken);
155: 
156:             uint oldEP2PBalance = _eP2PDToken.balanceOf(pool.dctDistributor);
157: 
158:             uint newEP2PBalance = LHelper.calEP2PDBalance(
159:                 _profileC.fsOf(poolOwner_),
160:                 _profileC.boosterOf(poolOwner_),
161:                 p2UDtoken.totalSupply() // <= FOUND
162:             );
163:             if (newEP2PBalance > oldEP2PBalance) {
164:                 _eP2PDToken.mint(pool.dctDistributor, newEP2PBalance - oldEP2PBalance);
165:             } else if (newEP2PBalance < oldEP2PBalance) {
166:                 _eP2PDToken.burn(pool.dctDistributor, oldEP2PBalance - newEP2PBalance);
167:             }
168:         }
169:     }

['96']

89:     function publicMint()
90:         external
91:     {
92:         uint mintingA = pendingA();
93:         if (mintingA == 0) {
94:             return;
95:         }
96:         if (totalSupply() + mintingA > MAX_SUPPLY) { // <= FOUND
97:             isMintingFinished = true;
98:             return;
99:         }
100:         _mint(_rewardPool, mintingA);
101:         _lastMintAt = block.timestamp;
102:         if (block.timestamp - _lastHalved >= HALVING_INTERVAL) {
103:             _tps = _tps / 2;
104:             _lastHalved = block.timestamp;
105:         }
106:     }

[Low-31] Unsafe use of transfer()/transferFrom() with IERC20

Resolution

SafeTransfer should be used in place of Transfer for Solidity contracts to ensure robust security and error handling. Unlike the basic Transfer function, SafeTransfer incorporates safeguards against potential smart contract vulnerabilities, such as reentrancy attacks and unexpected token loss. By automatically validating the recipient's ability to receive tokens and reverting transactions in case of failures,

Num of instances: 4

Findings

Click to show findings

['60']

60:     function _cashOut(
61:         address to_,
62:         uint amount_
63:     )
64:         internal
65:     {
66:         _token.transfer(to_, amount_); // <= FOUND
67:         _updateBalance();
68:     }

['95']

95:     function clean()
96:         public
97:         virtual
98:     {
99:         require(_isCleanEnabled, "unable to clean");
100:         uint currentBal = currentBalance();
101:         if (currentBal > _lastestBalance) {
102:             uint amount = currentBal - _lastestBalance; // <= FOUND
103:             _token.transfer(_cleanTo, amount); // <= FOUND
104:             emit Clean(amount); // <= FOUND
105:         }
106:         _updateBalance();
107:     }

['25']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     {
34:         uint vestingA = amount_ * vestingPercent_ / 10000;
35:         uint directA = amount_ - vestingA;
36:         _token.transfer(invester_, directA); // <= FOUND
37:         _token.transfer(address(_vester), vestingA);
38:         _vester.lock(invester_, vestingDur_, cliff_);
39:     }

['63']

63:     function withdraw(
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     {
70:         _token.transfer(dest_, amount_); // <= FOUND
71:     }

[Low-32] Large transfers may not work with some ERC20 tokens

Resolution

Large transfers with some ERC20 tokens may not work due to various reasons. Some tokens may have transfer restrictions built into the contract, such as daily transfer limits or maximum transfer sizes per transaction, to comply with regulatory requirements or to mitigate risks. Others may face issues with rounding errors when dealing with large quantities, especially if they have a high number of decimal places. Resolution involves carefully reading the token's contract to understand its constraints and behaviors and performing transfers accordingly. It may also be necessary to split large transfers into smaller increments if the token enforces specific transfer limits.

Num of instances: 4

Findings

Click to show findings

['60']

60:     function _cashOut(
61:         address to_,
62:         uint amount_
63:     )
64:         internal
65:     {
66:         _token.transfer(to_, amount_); // <= FOUND
67:         _updateBalance();
68:     }

['95']

95:     function clean()
96:         public
97:         virtual
98:     {
99:         require(_isCleanEnabled, "unable to clean");
100:         uint currentBal = currentBalance();
101:         if (currentBal > _lastestBalance) {
102:             uint amount = currentBal - _lastestBalance;
103:             _token.transfer(_cleanTo, amount); // <= FOUND
104:             emit Clean(amount);
105:         }
106:         _updateBalance();
107:     }

['25']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     {
34:         uint vestingA = amount_ * vestingPercent_ / 10000;
35:         uint directA = amount_ - vestingA;
36:         _token.transfer(invester_, directA); // <= FOUND
37:         _token.transfer(address(_vester), vestingA);
38:         _vester.lock(invester_, vestingDur_, cliff_);
39:     }

['63']

63:     function withdraw(
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     {
70:         _token.transfer(dest_, amount_); // <= FOUND
71:     }

[Low-33] Functions calling contracts/addresses with transfer hooks are missing reentrancy guards

Resolution

While adherence to the check-effects-interaction pattern is commendable, the absence of a reentrancy guard in functions, especially where transfer hooks might be present, can expose the protocol users to risks of read-only reentrancies. Such reentrancy vulnerabilities can be exploited to execute malicious actions even without altering the contract state. Without a reentrancy guard, the only potential mitigation would be to blocklist the entire protocol - an extreme and disruptive measure. Therefore, incorporating a reentrancy guard into these functions is vital to bolster security, as it helps protect against both traditional reentrancy attacks and read-only reentrancies, ensuring robust and safe protocol operations.

Num of instances: 8

Findings

Click to show findings

['60']

60:     function _cashOut( // <= FOUND
61:         address to_,
62:         uint amount_
63:     )
64:         internal
65:     {
66:         _token.transfer(to_, amount_); // <= FOUND
67:         _updateBalance();
68:     }

['95']

95:     function clean()
96:         public
97:         virtual
98:     {
99:         require(_isCleanEnabled, "unable to clean");
100:         uint currentBal = currentBalance();
101:         if (currentBal > _lastestBalance) {
102:             uint amount = currentBal - _lastestBalance; // <= FOUND
103:             _token.transfer(_cleanTo, amount); // <= FOUND
104:             emit Clean(amount);
105:         }
106:         _updateBalance();
107:     }

['25']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     {
34:         uint vestingA = amount_ * vestingPercent_ / 10000;
35:         uint directA = amount_ - vestingA; // <= FOUND
36:         _token.transfer(invester_, directA); // <= FOUND
37:         _token.transfer(address(_vester), vestingA);
38:         _vester.lock(invester_, vestingDur_, cliff_);
39:     }

['63']

63:     function withdraw( // <= FOUND
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     {
70:         _token.transfer(dest_, amount_); // <= FOUND
71:     }

['60']

60:     function _cashOut(
61:         address to_,
62:         uint amount_
63:     )
64:         internal
65:     {
66:         _token.transfer(to_, amount_); // <= FOUND
67:         _updateBalance();
68:     }

['95']

95:     function clean()
96:         public
97:         virtual
98:     {
99:         require(_isCleanEnabled, "unable to clean");
100:         uint currentBal = currentBalance();
101:         if (currentBal > _lastestBalance) {
102:             uint amount = currentBal - _lastestBalance;
103:             _token.transfer(_cleanTo, amount); // <= FOUND
104:             emit Clean(amount);
105:         }
106:         _updateBalance();
107:     }

['25']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     {
34:         uint vestingA = amount_ * vestingPercent_ / 10000;
35:         uint directA = amount_ - vestingA;
36:         _token.transfer(invester_, directA); // <= FOUND
37:         _token.transfer(address(_vester), vestingA); // <= FOUND
38:         _vester.lock(invester_, vestingDur_, cliff_);
39:     }

['63']

63:     function withdraw(
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     {
70:         _token.transfer(dest_, amount_); // <= FOUND
71:     }

[Low-34] Off-by-one timestamp error

Resolution

In Solidity, using >= or <= to compare against block.timestamp (alias now) may introduce off-by-one errors due to the fact that block.timestamp is only updated once per block and its value remains constant throughout the block's execution. If an operation happens at the exact second when block.timestamp changes, it could result in unexpected behavior. To avoid this, it's safer to use strict inequality operators (> or <). For instance, if a condition should only be met after a certain time, use block.timestamp > time rather than block.timestamp >= time. This way, potential off-by-one errors due to the exact timing of block mining are mitigated, leading to safer, more predictable contract behavior.

Num of instances: 2

Findings

Click to show findings

['89']

89:     function publicMint()
90:         external
91:     {
92:         uint mintingA = pendingA();
93:         if (mintingA == 0) {
94:             return;
95:         }
96:         if (totalSupply() + mintingA > MAX_SUPPLY) {
97:             isMintingFinished = true;
98:             return;
99:         }
100:         _mint(_rewardPool, mintingA);
101:         _lastMintAt = block.timestamp; // <= FOUND
102:         if (block.timestamp - _lastHalved >= HALVING_INTERVAL) { // <= FOUND
103:             _tps = _tps / 2;
104:             _lastHalved = block.timestamp;
105:         }
106:     }

['136']

136:     function createVote(
137:         address attacker_,
138:         address defender_,
139: 
140:         uint aEthValue_,
141:         uint dEthValue_,
142: 
143:         uint voterPercent_,
144:         uint aQuorum_,
145: 
146:         uint startedAt_,
147:         uint endAt_
148:     )
149:         external
150:         onlyAdmin
151:     {
152:         uint voteId = _totalVotes;
153:         _totalVotes++;
154:         SVote storage vote = _votes[voteId];
155:         vote.attacker = attacker_;
156:         vote.defender = defender_;
157: 
158:         vote.aEthValue = aEthValue_;
159:         vote.dEthValue = dEthValue_;
160: 
161:         _defenderEarningFreezedOf[defender_] += dEthValue_;
162: 
163:         LPercentage.validatePercent(voterPercent_);
164:         LPercentage.validatePercent(aQuorum_);
165:         vote.voterPercent = voterPercent_;
166:         vote.aQuorum = aQuorum_;
167:         uint inVal = _cashIn();
168:         require(aEthValue_ + dEthValue_ == inVal, "eth value incorrect");
169: 
170:         vote.startedAt = startedAt_; // <= FOUND
171:         require(startedAt_ >= block.timestamp, "must start in future"); // <= FOUND
172:         vote.endAt = endAt_;
173:         require(endAt_ >= startedAt_, "duration not negative");
174:         require(endAt_ < block.timestamp + 365 days, "duration too long");
175:         emit CreateVote(
176:             voteId,
177:             attacker_,
178:             defender_,
179:             aEthValue_,
180:             dEthValue_,
181:             voterPercent_,
182:             aQuorum_,
183:             startedAt_,
184:             endAt_
185:         );
186: 
187:         _addVoter(voteId, attacker_, true);
188:         _addVoter(voteId, defender_, false);
189:     }

[Low-35] Incorrect withdraw declaration

Resolution

In Solidity, it's essential for clarity and interoperability to correctly specify return types in function declarations. If the withdraw function is expected to return a bool to indicate success or failure, its omission could lead to ambiguity or unexpected behavior when interacting with or calling this function from other contracts or off-chain systems. Missing return values can mislead developers and potentially lead to contract integrations built on incorrect assumptions. To resolve this, the function declaration for withdraw should be modified to explicitly include the bool return type, ensuring clarity and correctness in contract interactions.

Num of instances: 3

Findings

Click to show findings

['63']

63:     function withdraw( // <= FOUND
64:         address account_,
65:         uint amount_,
66:         address dest_
67:     )
68:         external
69:         onlyAdmin
70:     

['63']

63:     function withdraw( // <= FOUND
64:         address account_,
65:         address poolOwner_,
66:         address dest_,
67:         uint amount_,
68:         bool isForced_
69:     )
70:         external
71:         onlyApprovedAdmin(account_)
72:     

['63']

63:     function withdraw( // <= FOUND
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     

[Low-36] Create methods are suspicious of the reorg attack

Resolution

"Create" methods, which deploy contracts via " = new ", are at risk from re-org attacks since the derived contract address is solely based on the Factory's nonce. Re-orgs, chain reorganizations, can occur across all EVM chains. The vulnerability amplifies when deploying contracts on EVM-compatible L2 solutions like Arbitrum and Polygon, which are notably susceptible to re-org attacks. Ethereum, a primary deployment target, has already experienced re-org events.

To bolster security against re-org threats, developers are advised to use the create2 method for contract deployments instead of the basic create. By using create2 with a salt that includes msg.sender, the contract's address derivation becomes more predictable and less prone to unexpected changes due to re-orgs. This strategy not only provides a more consistent deployment pattern but also minimizes risks associated with potential blockchain reorganizations.

Num of instances: 1

Findings

Click to show findings

['40']

27:     function initDCT(
28:         address accessControl_,
29:         address rewardPool_,
30:         address premineAddress_,
31:         uint256 premineAmount_,
32:         address cleanTo_
33:     )
34:         external
35:         initializer
36:     {
37:         initUseAccessControl(accessControl_);
38: 
39:         _rewardPool = rewardPool_;
40:         _vester = new Vester(); // <= FOUND
41:         _vester.initVester(accessControl_, address(this), cleanTo_);
42: 
43:         _mint(premineAddress_, premineAmount_);
44:     }

[NonCritical-1] Some if-statement can be converted to a ternary

Resolution

Improving code readability and compactness is an integral part of optimal programming practices. The use of ternary operators in place of if-else conditions is one such measure. Ternary operators allow us to write conditional statements in a more concise manner, thereby enhancing readability and simplicity. They follow the syntax condition ? exprIfTrue : exprIfFalse, which interprets as "if the condition is true, evaluate to exprIfTrue, else evaluate to exprIfFalse". By adopting this approach, we make our code more streamlined and intuitive, which could potentially aid in better understanding and maintenance of the codebase.

Num of instances: 48

Findings

Click to show findings

['80']

80:         if (holder.regBal > bal) {
81:             reward = deltaRpt * bal / MFACTOR; // <= FOUND
82:         }

['216']

216:         if (timeDiff > _maxSponsorAfter) {
217:             timeDiff = _maxSponsorAfter; // <= FOUND
218:         }

['334']

334:         if (rd + duration_ > _maxDuration) {
335:             duration_ = _maxDuration - rd; // <= FOUND
336:         }

['42']

42:         if (isSelfStake_) {
43:             rs = rs * selfStakeAdvantage_ / LPercentage.DEMI; // <= FOUND
44:         }

['70']

70:         if (max < earningBalance_) {
71:             max = earningBalance_; // <= FOUND
72:         }

['117']

117:         if (leverage < LPercentage.DEMI) {
118:             leverage = LPercentage.DEMI; // <= FOUND
119:         }

['105']

105:         if (pastTime > duration) {
106:             pastTime = duration; // <= FOUND
107:         }

['69']

69:         if (lockData.duration < duration) {
70:             lockData.duration = duration; // <= FOUND
71:         }

['227']

227:         if (power == 0) {
228:             
229:             power = 1; // <= FOUND
230:         }

['232']

232:         if (isForAttacker_) {
233:             vote.attackerPower += power; // <= FOUND
234:         }

['85']

85:            if (regSupply == 0) {
86:                 rpt = 0; // <= FOUND
87:                 
88:             }

['604']

604:            if (realDuration > _maxDuration) {
605:                 realDuration = _maxDuration; // <= FOUND
606:             }

['200']

200:           if (vote.isForAttacker[voter_]) {
201:                 vote.attackerPower -= power; // <= FOUND
202:             }

['101']

101:         if (holder.regBal != bal) { // <= FOUND
102:             emit UpdateRegBal(holder_, bal); // <= FOUND
103:         }

['205']

205:        if (poolOwner_ == staker_) { // <= FOUND
206:             return;
207:         }

['209']

209:         if (profile.sponsor == staker_) { // <= FOUND
210:             return;
211:         }

['404']

404:        if (minWstethA_ > 0) { // <= FOUND
405:             LLido.allToWsteth(minWstethA_); // <= FOUND
406:         }

['407']

407:         if (wstethA_ > 0) { // <= FOUND
408:             _geth.transferFrom(msg.sender, address(this), wstethA_); // <= FOUND
409:         }

['505']

505:         if (bountyPullerTo_ == account_) { // <= FOUND
506:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this))); // <= FOUND
507:         }

['704']

704:         if (!isFinalizedBefore) { // <= FOUND
705:             _reCalFs(vote.defender); // <= FOUND
706:         }

['64']

64:        if (msg.sender != address(_vester)) { // <= FOUND
65:            _vester.unlock(from_); // <= FOUND
66:         }

['93']

93:         if (mintingA == 0) { // <= FOUND
94:             return;
95:         }

['75']

75:         if (from_ == to_) { // <= FOUND
76:             return;
77:         }

['79']

79:         if (amount == 0) { // <= FOUND
80:             return;
81:         }

['91']

91:         if (_sharedA[account_] > _maxEarningOf[account_]) { // <= FOUND
92:             _updateMaxEarning(account_, _sharedA[account_]); // <= FOUND
93:         }

['111']

111:             if (_sharedA[account_] > _maxEarningOf[account_]) { // <= FOUND
112:                 _updateMaxEarning(account_, _sharedA[account_]); // <= FOUND
113:             }

['152']

152:         if (amount == 0) { // <= FOUND
153:             return _sharedA[account_];
154:         }

['158']

158:         if (poolConfig.isInitialized) { // <= FOUND
159:             return false;
160:         }

['38']

38:         if (lockTime_ == 0) { // <= FOUND
39:             require(rd > 0, "already unlocked"); // <= FOUND
40:         }

['32']

32:         if (max == 0) { // <= FOUND
33:             return LPercentage.DEMI;
34:         }

['205']

205:        if (wethA_ == 0) { // <= FOUND
206:             return;
207:         }

['241']

241:        if (wstethA_ == 0) { // <= FOUND
242:             return;
243:         }

['256']

256:         if (amount > 0) { // <= FOUND
257:             weth.withdraw(amount); // <= FOUND
258:         }

['146']

146:         if (amount > 0) { // <= FOUND
147:             weth.deposit{value: amount}(); // <= FOUND
148:         }

['32']

32:        if (lockData_.startedAt > block.timestamp) { // <= FOUND
33:             return lockData_.duration + lockData_.startedAt - block.timestamp;
34:         }

['36']

36:         if (pastTime < lockData_.duration) { // <= FOUND
37:             return lockData_.duration - pastTime;
38:         }

['100']

100:         if (!isForced_) { // <= FOUND
101:             require(LLocker.isUnlocked(lockData, fs, isPoolOwner), "not unlocked"); // <= FOUND
102:         }

['113']

113:         if (total != receivedA) { // <= FOUND
114:             _cashOut(_penaltyAddress, total - receivedA); // <= FOUND
115:         }

['110']

110:        if (_needAthRecord) { // <= FOUND
111:             _updateAthBalance(to_, balanceOf(to_)); // <= FOUND
112:         }

['57']

57:         if (profileData.sponsor == address(0x0)) { // <= FOUND
58:             _initDefaultSPercent(account_); // <= FOUND
59:         }

['86']

86:         if (sPercent_ >  profileData.sPercent) { // <= FOUND
87:             _updateSponsor(account, profileData.sponsor); // <= FOUND
88:         }

['89']

89:         if (toTransferA > 0) { // <= FOUND
90:             _cashOut(account_, toUnlockA + airdrop); // <= FOUND
91:         }

['232']

232:         if (isForAttacker_) { // <= FOUND
233:             vote.attackerPower += power;
234:         }

['306']

306:         if (!vote.isFinalized) { // <= FOUND
307:             _tryFinalize(voteId_); // <= FOUND
308:         }

['163']

163:            if (newEP2PBalance > oldEP2PBalance) { // <= FOUND
164:                 _eP2PDToken.mint(pool.dctDistributor, newEP2PBalance - oldEP2PBalance); // <= FOUND
165:             }

['364']

364:             if (isFirstStake) { // <= FOUND
365:                 IDistributor(pool.ethDistributor).distribute(); // <= FOUND
366:             }

['200']

200:           if (vote.isForAttacker[voter_]) { // <= FOUND
201:                 vote.attackerPower -= power;
202:             }

['308']

308:               if (account_ == poolOwner_) { // <= FOUND
309:                     
310:                     _profileC.updateSponsor(account_, account_); // <= FOUND
311:                 }

[NonCritical-2] Addresses shouldn't be hard-coded

Num of instances: 4

Findings

Click to show findings

['136']

136:     ISwapRouter private constant router =
137:         ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); // <= FOUND

['138']

138:     IWETH internal constant weth =
139:         IWETH(0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6); // <= FOUND

['152']

152:     INonfungiblePositionManager private constant nonfungiblePositionManager =
153:         INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88); // <= FOUND

['160']

160:         IUniswapV3Factory factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984); // <= FOUND

[NonCritical-3] Subtraction may underflow if multiplication is too large

Resolution

In arithmetic operations involving subtraction and multiplication, an underflow may occur if a subtraction result is negative, or if a multiplication result exceeds the maximum value representable in the data type. For instance, if a large multiplication precedes a subtraction, it may create a value too large to subtract from, causing an underflow. This could lead to unexpected and incorrect results in the calculation.

Num of instances: 1

Findings

Click to show findings

['96']

96:     function _setAdjReward(
97:         address account_,
98:         uint prevReward,
99:         uint reward_,
100:         uint dTokenBalance_
101:     )
102:         internal
103:     {
104:         _adjustedRewardOf[account_] = int256(dTokenBalance_ * _ptr) - int256(reward_ * MFACTOR); // <= FOUND
105:         emit SetReward(account_, prevReward, reward_);
106:     }

[NonCritical-4] Avoid commenting out unused code in production

Resolution

Commenting out lines of code or entire functions in production code can lead to confusion and misinterpretation. This is because it becomes unclear if the commented-out code is intended for future use, is deprecated, or is just an artifact of previous development stages. Such practices can lead to codebase clutter and make it harder for other developers to understand and maintain the code. Furthermore, commented-out code could contain security vulnerabilities that might be overlooked because it is not active. The resolution is to always clean up your code before deploying it to production. Remove any non-essential comments, debug lines, and unused functions. If there is a need to preserve certain stages of your code, use version control systems like Git, which are designed to track and revert to previous versions of the code when needed.

Num of instances: 1

Findings

Click to show findings

['28']

28: //   function initClaimableGas() internal { // <= FOUND

[NonCritical-5] Code does not follow the best practice of check-effects-interaction

Resolution

The "check-effects-interaction" pattern is a best practice in smart contract development, emphasizing the order of operations in functions to prevent reentrancy attacks. Violations arise when a function interacts with external contracts before settling internal state changes or checks. This misordering can expose the contract to potential threats. To adhere to this pattern, first ensure all conditions or checks are satisfied, then update any internal states, and only after these steps, interact with external contracts or addresses. Rearranging operations to this recommended sequence bolsters contract security and aligns with established best practices in the Ethereum community.

Num of instances: 1

Findings

Click to show findings

['253']

253:     function _tryFinalize(
254:         uint voteId_
255:     )
256:         internal
257:     {
258:         SVote storage vote = _votes[voteId_];
259:         require(!vote.isFinalized, "already finalized");
260:         require(vote.endAt < block.timestamp, "vote not ended");
261: 
262:         vote.isFinalized = true;
263:         uint totalPower = vote.attackerPower + vote.defenderPower;
264:         uint reqPower = LPercentage.getPercentA(totalPower, vote.aQuorum);
265:         vote.isAttackerWon = vote.attackerPower > reqPower;
266: 
267:         if (vote.isAttackerWon) {
268:             vote.winVal = LPercentage.getPercentA(vote.dEthValue, vote.voterPercent);
269:             vote.winnerPower = vote.attackerPower;
270:             uint toWinnerVal = vote.aEthValue + vote.dEthValue - vote.winVal;
271:             address payable to = payable(vote.attacker);
272:             
273:             LLido.sellWsteth(toWinnerVal);
274:             LLido.wethToEth();
275:             to.send(address(this).balance); // <= FOUND
276:             clean();
277:         } else {
278:             vote.winVal = LPercentage.getPercentA(vote.aEthValue, vote.voterPercent);
279:             vote.winnerPower = vote.defenderPower;
280:             uint unfreezeVal = vote.dEthValue;
281:             uint rewardWinnerVal = vote.aEthValue - vote.winVal;
282:             address payable to = payable(vote.defender);
283: 
284:             _eEarning.clean();
285:             
286:             _cashOut(address(_eEarning), unfreezeVal);
287:             _eEarning.update(to, false);
288:             
289:             _cashOut(address(_eEarning), rewardWinnerVal);
290:             _eEarning.update(to, true);
291:         }
292:         _defenderEarningFreezedOf[vote.defender] -= vote.dEthValue;
293: 
294:         emit Finalize(voteId_, vote.isAttackerWon, vote.winnerPower, vote.winVal);
295:     }

[NonCritical-6] Events may be emitted out of order due to code not follow the best practice of check-effects-interaction

Resolution

The "check-effects-interaction" pattern also impacts event ordering. When a contract doesn't adhere to this pattern, events might be emitted in a sequence that doesn't reflect the actual logical flow of operations. This can cause confusion during event tracking, potentially leading to erroneous off-chain interpretations. To rectify this, always ensure that checks are performed first, state modifications come next, and interactions with external contracts or addresses are done last. This will ensure events are emitted in a logical, consistent manner, providing a clear and accurate chronological record of on-chain actions for off-chain systems and observers.

Num of instances: 1

Findings

Click to show findings

['253']

253:     function _tryFinalize(
254:         uint voteId_
255:     )
256:         internal
257:     {
258:         SVote storage vote = _votes[voteId_];
259:         require(!vote.isFinalized, "already finalized");
260:         require(vote.endAt < block.timestamp, "vote not ended");
261: 
262:         vote.isFinalized = true;
263:         uint totalPower = vote.attackerPower + vote.defenderPower;
264:         uint reqPower = LPercentage.getPercentA(totalPower, vote.aQuorum);
265:         vote.isAttackerWon = vote.attackerPower > reqPower;
266: 
267:         if (vote.isAttackerWon) {
268:             vote.winVal = LPercentage.getPercentA(vote.dEthValue, vote.voterPercent);
269:             vote.winnerPower = vote.attackerPower;
270:             uint toWinnerVal = vote.aEthValue + vote.dEthValue - vote.winVal;
271:             address payable to = payable(vote.attacker);
272:             
273:             LLido.sellWsteth(toWinnerVal);
274:             LLido.wethToEth();
275:             to.send(address(this).balance); // <= FOUND
276:             clean();
277:         } else {
278:             vote.winVal = LPercentage.getPercentA(vote.aEthValue, vote.voterPercent);
279:             vote.winnerPower = vote.defenderPower;
280:             uint unfreezeVal = vote.dEthValue;
281:             uint rewardWinnerVal = vote.aEthValue - vote.winVal;
282:             address payable to = payable(vote.defender);
283: 
284:             _eEarning.clean();
285:             
286:             _cashOut(address(_eEarning), unfreezeVal);
287:             _eEarning.update(to, false);
288:             
289:             _cashOut(address(_eEarning), rewardWinnerVal);
290:             _eEarning.update(to, true);
291:         }
292:         _defenderEarningFreezedOf[vote.defender] -= vote.dEthValue;
293: 
294:         emit Finalize(voteId_, vote.isAttackerWon, vote.winnerPower, vote.winVal); // <= FOUND
295:     }

[NonCritical-7] Unnecessary struct attribute prefix

Resolution

In struct definitions, using redundant prefixes for attributes is unnecessary. For instance, in a struct named Employee, attributes like employeeName, employeeID, and employeeEmail can be simplified to name, ID, and email respectively, since they are already inherently associated with Employee. By removing these repetitive prefixes, the code becomes more concise and easier to read, maintaining its contextual clarity.

Num of instances: 1

Findings

Click to show findings

['5']

5:     struct SProfile{ // <= FOUND
6:         address sponsor; // <= FOUND
7:         uint sPercent; // <= FOUND
8:         uint nextSPercent;
9:         uint updatedAt;
10:         uint ifs;
11:         uint bonusBooster;
12:     }

[NonCritical-8] Contracts should have all public/external functions exposed by interfaces

Resolution

Contracts should expose all public and external functions through interfaces. This practice ensures a clear and consistent definition of how the contract can be interacted with, promoting better transparency and integration.

Num of instances: 122

Findings

Click to show findings

['52']

52:     function addAdmins(
53:         address[] memory accounts_
54:     )
55:         external
56:         onlyOwner
57:     

['65']

65:     function removeAdmins(
66:         address[] memory accounts_
67:     )
68:         external
69:         onlyOwner
70:     

['78']

78:     function pause()
79:         external
80:         onlyOwner
81:     

['85']

85:     function unpause()
86:         external
87:         onlyOwner
88:     

['95']

95:     function isOwner(
96:         address account_
97:     )
98:         external
99:         view
100:         returns(bool)
101:     

['104']

104:     function isAdmin(
105:         address account_
106:     )
107:         external
108:         view
109:         returns(bool)
110:     

['114']

114:     function isPaused()
115:         external
116:         view
117:         returns(bool)
118:     

['109']

109:     function cleanTo()
110:         external
111:         view
112:         returns(address)
113:     

['108']

108:     function initController(
109:         address[] memory contracts_
110:     )
111:         external
112:         initializer
113:     

['589']

589:     function earningReinvest(
590:         bool isEth_,
591:         address payable poolOwner_,
592:         uint duration_,
593:         uint amount_,
594:         
595:         uint minSPercent_,
596:         uint poolConfigCode_
597:     )
598:         external
599:     

['641']

641:     function createVote(
642:         address defender_,
643:         uint dEthValue_,
644:         uint voterPercent_,
645:         uint freezeDuration_,
646:         uint minWstethA_,
647:         uint wstethA_
648:     )
649:         external
650:         payable
651:     

['693']

693:     function votingClaimFor(
694:         uint voteId_,
695:         address voter_
696:     )
697:         external
698:     

['733']

733:     function updateConfigs(
734:         uint[] memory values_
735:     )
736:         external
737:         onlyAdmin
738:     

['27']

27:     function initDCT(
28:         address accessControl_,
29:         address rewardPool_,
30:         address premineAddress_,
31:         uint256 premineAmount_,
32:         address cleanTo_
33:     )
34:         external
35:         initializer
36:     

['46']

46:     function start()
47:         external
48:         onlyOwner
49:     

['69']

69:     function tps()
70:         external
71:         view
72:         returns(uint)
73:     

['89']

89:     function publicMint()
90:         external
91:     

['108']

108:     function lastMintAt()
109:         external
110:         view
111:         returns(uint)
112:     

['116']

116:     function lastHalved()
117:         external
118:         view
119:         returns(uint)
120:     

['124']

124:     function rewardPool()
125:         external
126:         view
127:         returns(address)
128:     

['132']

132:     function vester()
133:         external
134:         view
135:         returns(address)
136:     

['144']

144:     function changeRewardPool(
145:         address rewardPool_
146:     )
147:         external
148:         onlyAdmin
149:     

['66']

66:     function beforeTokenTransfer(
67:         address from_,
68:         address to_,
69:         uint256 amount_
70:     )
71:         external
72:         onlyDToken
73:     

['21']

21:     function distribute()
22:         external
23:     

['138']

138:     function claimFor(
139:         address account_,
140:         address dest_
141:     )
142:         external
143:         onlyAdmin
144:     

['63']

63:     function updateMaxEarning(
64:         address account_,
65:         uint maxEarning_
66:     )
67:         external
68:         onlyAdmin
69:     

['28']

28:     function update(
29:         address account_,
30:         bool needShareComm_
31:     )
32:         external
33:     

['117']

117:     function withdraw(
118:         address account_,
119:         uint amount_,
120:         address dest_
121:     )
122:         external
123:         onlyAdmin
124:     

['48']

48:     function maxEarningOf(
49:         address account_
50:     )
51:         external
52:         view
53:         returns(uint)
54:     

['41']

41:     function earningOf(
42:         address account_
43:     )
44:         external
45:         view
46:         returns(uint)
47:     

['39']

39:     function initEthSharing(
40:         address accessControl_,
41:         uint devTeamPercent_,
42:         uint defaultOwnerPercent_,
43:         uint defaultUserPercent_,
44:         bool inDefaultOnlyMode_
45: 
46:     )
47:         external
48:         initializer
49:     

['122']

122:     function configSystem(
123:         uint devTeamPercent_,
124:         uint defaultOwnerPercent_,
125:         uint defaultUserPercent_,
126:         bool inDefaultOnlyMode_
127:     )
128:         external
129:         onlyAdmin
130:     

['139']

139:     function tryResetPool(
140:         address poolOwner_
141:     )
142:         external
143:         onlyAdmin
144:     

['150']

150:     function initPoolConfig(
151:         address poolOwner_
152:     )
153:         external
154:         onlyAdmin
155:         returns(bool)
156:     

['34']

34:     function configPool(
35:         uint ownerPercent_,
36:         uint userPercent_
37:     )
38:         external
39:     

['59']

59:     function lock(
60:         address account_,
61:         address poolOwner_,
62:         uint duration_
63:     )
64:         external
65:         onlyAdmin
66:     

['74']

74:     function withdraw(
75:         address account_,
76:         address poolOwner_,
77:         address dest_,
78:         uint amount_,
79:         bool isForced_
80:     )
81:         external
82:         onlyApprovedAdmin(account_)
83:     

['130']

130:     function updatePenaltyAddress(
131:         address penaltyAddress_
132:     )
133:         external
134:         onlyOwner
135:     

['33']

33:     function penaltyAddress()
34:         external
35:         view
36:         returns(address)
37:     

['38']

38:     function getLockId(
39:         address account_,
40:         address poolOwner_
41:     )
42:         external
43:         pure
44:         returns(bytes32)
45:     

['46']

46:     function getLockDataById(
47:         bytes32 lockId_
48:     )
49:         external
50:         view
51:         returns(LLocker.SLock memory)
52:     

['53']

53:     function getLockData(
54:         address account_,
55:         address poolOwner_
56:     )
57:         external
58:         view
59:         returns(LLocker.SLock memory)
60:     

['38']

38:     function athBalance()
39:         external
40:         view
41:         returns(uint)
42:     

['39']

39:     function needAthRecord()
40:         external
41:         view
42:         returns(bool)
43:     

['82']

82:     function activeAthRecord()
83:         external
84:         onlyAdmin
85:     

['89']

89:     function deactiveAthRecord()
90:         external
91:         onlyAdmin
92:     

['146']

146:     function setInPrivateMode(
147:         bool inPrivateMode_
148:     )
149:         external
150:         onlyOwner
151:     

['155']

155:     function mint(
156:         address account_,
157:         uint amount_
158:     )
159:         external
160:         onlyAdmin
161:     

['35']

35:     function initPoolFactory(
36:         address accessControl_,
37:         address geth_,
38:         address dct_
39:     )
40:         external
41:         initializer
42:     

['104']

104:     function createPool(
105:         address owner_
106:     )
107:         external
108:         onlyAdmin
109:     

['114']

114:     function isCreated(
115:         address owner_
116:     )
117:         external
118:         view
119:         returns(bool)
120:     

['124']

124:     function getPool(
125:         address owner_
126:     )
127:         external
128:         view
129:         returns(SPool memory)
130:     

['14']

14:     function config(
15:         IVester vester_,
16:         IERC20 token_
17:     )
18:         external
19:         onlyOwner
20:     

['41']

41:     function transferLock(
42:         address from_,
43:         address to_,
44:         uint amount_
45:     )
46:         external
47:         onlyOwner
48:     

['63']

63:     function withdraw(
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     

['73']

73:     function sendTos(
74:         address[] memory investers_,
75:         uint[] memory amounts_,
76:         uint[] memory vestingDurs_,
77:         uint[] memory vestingPercents_,
78:         uint[] memory cliffs_
79:     )
80:         external
81:         onlyOwner
82:     

['96']

96:     function token()
97:         external
98:         view
99:         returns(address)
100:     

['40']

40:     function initProfile(
41:         address accessControl_
42:     )
43:         external
44:         initializer
45:     

['66']

66:     function updateSponsor(
67:         address account_,
68:         address sponsor_
69:     )
70:         external
71:         onlyAdmin
72:     

['35']

35:     function setSPercent(
36:         uint sPercent_
37:     )
38:         external
39:     

['102']

102:     function setDefaultSPercentConfig(
103:         uint sPercent_
104:     )
105:         external
106:         onlyAdmin
107:     

['112']

112:     function setMinSPercentConfig(
113:         uint sPercent_
114:     )
115:         external
116:         onlyAdmin
117:     

['122']

122:     function updateFsOf(
123:         address account_,
124:         uint fs_
125:     )
126:         external
127:         onlyAdmin
128:     

['134']

134:     function updateBoosterOf(
135:         address account_,
136:         uint booster_
137:     )
138:         external
139:         onlyAdmin
140:     

['20']

20:     function profileOf(
21:         address account_
22:     )
23:         external
24:         view
25:         returns(SProfile memory)
26:     

['27']

27:     function getSponsorPart(
28:         address account_,
29:         uint amount_
30:     )
31:         external
32:         view
33:         returns(address sponsor, uint sAmount)
34:     

['62']

62:     function fsOf(
63:         address account_
64:     )
65:         external
66:         view
67:         returns(uint)
68:     

['69']

69:     function boosterOf(
70:         address account_
71:     )
72:         external
73:         view
74:         returns(uint)
75:     

['83']

83:     function approveAdmin(
84:         address admin_
85:     )
86:         external
87:     

['95']

95:     function revokeAdmin(
96:         address admin_
97:     )
98:         external
99:     

['107']

107:     function isApprovedAdmin(
108:         address account_,
109:         address admin_
110:     )
111:         external
112:         view
113:         returns(bool)
114:     

['35']

35:     function lock(
36:         address account_,
37:         uint duration_,
38:         uint cliff_
39:     )
40:         external
41:         onlyAdmin
42:     

['52']

52:     function transferLock(
53:         address from_,
54:         address to_,
55:         uint amount_
56:     )
57:         external
58:         onlyAdmin
59:     

['123']

123:     function getUnlockedA(
124:         address account_
125:     )
126:         external
127:         view
128:         returns(uint)
129:     

['135']

135:     function getLockData(
136:         address account_
137:     )
138:         external
139:         view
140:         returns(LLocker.SLock memory)
141:     

['119']

119:     function initVoting(
120:         address accessControl_,
121:         address votingTokenAddr_,
122:         address gethAddr_,
123:         address eEarningAddr_,
124:         address cleanTo_
125:     )
126:         external
127:         initializer
128:     

['136']

136:     function createVote(
137:         address attacker_,
138:         address defender_,
139: 
140:         uint aEthValue_,
141:         uint dEthValue_,
142: 
143:         uint voterPercent_,
144:         uint aQuorum_,
145: 
146:         uint startedAt_,
147:         uint endAt_
148:     )
149:         external
150:         onlyAdmin
151:     

['242']

242:     function updatePower(
243:         uint voteId_,
244:         bool isForAttacker_
245:     )
246:         external
247:         onlyInVotingTime(voteId_)
248:     

['297']

297:     function claimFor(
298:         uint voteId_,
299:         address voter_
300:     )
301:         external
302:         onlyAdmin
303:     

['327']

327:     function closeVote(
328:         uint voteId_,
329:         address dest_
330:     )
331:         external
332:         onlyAdmin
333:     

['344']

344:     function getVote(
345:         uint voteId_
346:     )
347:         external
348:         view
349:         returns(IVoting.SVoteBasicInfo memory)
350:     

['373']

373:     function defenderEarningFreezedOf(
374:         address account_
375:     )
376:         external
377:         view
378:         returns(uint)
379:     

['40']

40:     function init(
41:         address dToken_,
42:         address rewardToken_
43:     )
44:         public
45:         initializer
46:     

['51']

51:     function distribute()
52:         public
53:     

['63']

63:     function claim()
64:         public
65:     

['69']

69:     function claimFor(
70:         address holder_
71:     )
72:         public
73:     

['110']

110:     function claimableOf(
111:         address holder_
112:     )
113:         public
114:         view
115:         returns(uint reward)
116:     

['117']

117:     function currentBalance()
118:         public
119:         view
120:         returns(uint)
121:     

['125']

125:     function lastestBalance()
126:         public
127:         view
128:         returns(uint)
129:     

['412']

412:     function ethStake(
413:         address payable poolOwner_,
414:         uint duration_,
415:         uint minSPercent_,
416:         uint poolConfigCode_,
417:         uint minWstethA_,
418:         uint wstethA_
419:     )
420:         public
421:         payable
422:         tryPublicMint
423:     

['429']

429:     function dctStake(
430:         uint amount_,
431:         address payable poolOwner_,
432:         uint duration_
433:     )
434:         public
435:         payable
436:         tryPublicMint
437:     

['478']

478:     function earningPulls(
479:         address account_,
480:         address[] memory poolOwners_,
481:         address bountyPullerTo_
482:     )
483:         public
484:         tryPublicMint
485:     

['543']

543:     function lockWithdraw(
544:         bool isEth_,
545:         address payable poolOwner_,
546:         uint amount_,
547:         address payable dest_,
548:         bool isForced_,
549:         uint minEthA_
550:     )
551:         public
552:         tryPublicMint
553:     

['615']

615:     function earningWithdraw(
616:         bool isEth_,
617:         uint amount_,
618:         address payable dest_,
619:         uint minEthA_
620:         
621:     )
622:         public
623:     

['712']

712:     function earningWithdrawDevTeam()
713:         public
714:     

['721']

721:     function claimRevenueShareDevTeam()
722:         public
723:     

['77']

77:     function pendingA()
78:         public
79:         view
80:         returns(uint)
81:     

['14']

14:     function initDToken(
15:         address accessControl_,
16:         bool inPrivateMode_,
17:         string memory name_,
18:         string memory symbol_,
19:         address[] memory distributorAddrs_
20:     )
21:         public
22:         initializer
23:     

['34']

34:     function initDistributor(
35:         address accessControl_,
36:         address dToken_,
37:         address rewardToken_
38:     )
39:         public
40:         initializer
41:     

['114']

114:     function calReward(
115:         uint ptr_,
116:         uint dTokenBalance_,
117:         int256 adjustedReward_
118:     )
119:         public
120:         pure
121:         returns(uint)
122:     

['128']

128:     function rewardOf(
129:         address account_
130:     )
131:         public
132:         view
133:         returns(uint)
134:     

['73']

73:     function shareCommission(
74:         address account_
75:     )
76:         public
77:     

['59']

59:     function getCode(
60:         uint ownerPercent_,
61:         uint userPercent_
62:     )
63:         public
64:         pure
65:         returns(uint)
66:     

['70']

70:     function getPoolCode(
71:         address poolOwner_
72:     )
73:         public
74:         view
75:         returns(uint)
76:     

['175']

175:     function getPoolLockPercent(
176:         address poolOwner_
177:     )
178:         public
179:         view
180:         returns(uint)
181:     

['186']

186:     function getDevTeamPart(
187:         uint value_
188:     )
189:         public
190:         view
191:         returns(uint)
192:     

['197']

197:     function getSysExcPart(
198:         uint value_
199:     )
200:         public
201:         view
202:         returns(uint)
203:     

['208']

208:     function getPoolOwnerPart(
209:         address poolOwner_,
210:         uint value_
211:     )
212:         public
213:         view
214:         returns(uint)
215:     

['222']

222:     function getPoolUserPart(
223:         address poolOwner_,
224:         uint value_
225:     )
226:         public
227:         view
228:         returns(uint)
229:     

['236']

236:     function getLockedPart(
237:         address poolOwner_,
238:         uint value_
239:     )
240:         public
241:         view
242:         returns(uint)
243:     

['251']

251:     function getSharingParts(
252:         address poolOwner_,
253:         uint value_,
254:         uint code_
255:     )
256:         public
257:         view
258:         returns(uint devTeamA, uint poolOwnerA, uint poolUserA, uint lockedA)
259:     

['200']

200:     function buyWsteth(
201:         uint wethA_
202:     )
203:         public
204:     

['216']

216:     function allToWsteth(
217:         uint minWstethBal_
218:     )
219:         public
220:     

['226']

226:     function allToEth(
227:         uint minEthBal_
228:     )
229:         public
230:     

['236']

236:     function sellWsteth(
237:         uint wstethA_
238:     )
239:         public
240:     

['252']

252:     function wethToEth()
253:         public
254:     

['261']

261:     function ethToWeth()
262:         public
263:     

['42']

42:     function initLocker(
43:         address accessControl_,
44:         address token_,
45:         address profileCAddr_,
46:         address penaltyAddress_,
47:         address cleanTo_
48:     )
49:         public
50:         initializer
51:     

['73']

73:     function initUseAccessControl(
74:         address accessControl_
75:     )
76:         public
77:         initializer
78:     

['23']

23:     function initVester(
24:         address accessControl_,
25:         address token_,
26:         address cleanTo_
27:     )
28:         public
29:         initializer
30:     

['75']

75:     function unlock(
76:         address account_
77:     )
78:         public
79:     

['109']

109:     function currentLockData(
110:         address account_
111:     )
112:         public
113:         view
114:         returns(uint restA, uint restDuration)
115:     

[NonCritical-9] Using abi.encodePacked can result in hash collision when used in hashing functions

Resolution

Consider using abi.encode as this pads data to 32 byte segments

Num of instances: 1

Findings

Click to show findings

['22']

22:         return keccak256(abi.encodePacked(account_, poolOwner_)); // <= FOUND

[NonCritical-10] Non constant/immutable state variables are missing a setter post deployment

Resolution

Non-constant or non-immutable state variables lacking a setter function can create inflexibility in contract operations. If there's no way to update these variables post-deployment, the contract might not adapt to changing conditions or requirements, which can be a significant drawback, especially in upgradable or long-lived contracts. To resolve this, implement setter functions guarded by appropriate access controls, like onlyOwner or similar modifiers, so that these variables can be updated as required while maintaining security. This enables smoother contract maintenance and feature upgrades.

Num of instances: 3

Findings

Click to show findings

['78']

78: uint public _minDuration = 30 days;

['79']

79: uint public _maxDuration = 720 days;

['87']

87: uint public _mintingInt = 7;

[NonCritical-11] Require statements should have error string

Resolution

Adding error strings to require statements in Solidity contracts, although not mandatory, enhances error handling, debugging, and overall contract maintainability. Providing a descriptive error message with each require statement helps identify the specific reason for a transaction failure, making it easier for developers to troubleshoot issues and for users to understand the cause of a revert. Including error strings improves code readability and fosters transparency, as the logic and conditions behind each requirement are clearly communicated

Num of instances: 1

Findings

Click to show findings

['659']

659: require(defender_ != address(_devTeam));

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

Resolution

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

Num of instances: 2

Findings

Click to show findings

['83']

73:     function sendTos(
74:         address[] memory investers_,
75:         uint[] memory amounts_,
76:         uint[] memory vestingDurs_,
77:         uint[] memory vestingPercents_,
78:         uint[] memory cliffs_
79:     )
80:         external
81:         onlyOwner
82:     {
83:         for(uint i = 0; i < investers_.length; i++) { // <= FOUND
84:             _sendTo(investers_[i], amounts_[i], vestingDurs_[i], vestingPercents_[i], cliffs_[i]);
85:         }
86:     }

['501']

478:     function earningPulls(
479:         address account_,
480:         address[] memory poolOwners_,
481:         address bountyPullerTo_
482:     )
483:         public
484:         tryPublicMint
485:     {
486:         _dEarning.clean();
487:         _eEarning.clean();
488: 
489:         
490:         _dP2PDistributor.distribute();
491: 
492:         
493:         
494: 
495:         
496:         
497: 
498:         _distributorClaimFor(address(_eDP2PDistributor), account_, address(this));
499:         _distributorClaimFor(address(_dDP2PDistributor), account_, address(_dEarning));
500: 
501:         for(uint i = 0; i < poolOwners_.length; i++) { // <= FOUND
502:             _earningPull(account_, poolOwners_[i]);
503:         }
504: 
505:         if (bountyPullerTo_ == account_) {
506:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
507:         } else {
508:             uint256 amountForPuller = _geth.balanceOf(address(this)) * _bountyPullEarningPercent / LPercentage.DEMI;
509: 
510:             LLido.sellWsteth(amountForPuller);
511:             LLido.wethToEth();
512:             payable(bountyPullerTo_).transfer(address(this).balance);
513: 
514:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
515:         }
516: 
517:         _dEarning.update(account_, true);
518:         _eEarning.update(account_, true);
519: 
520:         _reCalFs(account_);
521:     }

[NonCritical-13] Using zero as a parameter

Resolution

Taking 0 as a valid argument in Solidity without checks can lead to severe security issues. A historical example is the infamous 0x0 address bug where numerous tokens were lost. This happens because '0' can be interpreted as an uninitialized address, leading to transfers to the '0x0' address, effectively burning tokens. Moreover, 0 as a denominator in division operations would cause a runtime exception. It's also often indicative of a logical error in the caller's code. It's important to always validate input and handle edge cases like 0 appropriately. Use require() statements to enforce conditions and provide clear error messages to facilitate debugging and safer code.

Num of instances: 7

Findings

Click to show findings

['429']

429:     function dctStake(
430:         uint amount_,
431:         address payable poolOwner_,
432:         uint duration_
433:     )
434:         public
435:         payable
436:         tryPublicMint
437:     {
438:         _dct.transferFrom(msg.sender, address(this), amount_);
439:         uint taxA = LPercentage.getPercentA(amount_, _dctTaxPercent);
440:         _dct.transfer(address(0xdead), taxA);
441:         bool isEth = false;
442:         
443:         uint poolConfigCode = 0;
444:         _stake(isEth, msg.sender, poolOwner_, duration_, 0, poolConfigCode); // <= FOUND
445:     }

['589']

589:     function earningReinvest(
590:         bool isEth_,
591:         address payable poolOwner_,
592:         uint duration_,
593:         uint amount_,
594:         
595:         uint minSPercent_,
596:         uint poolConfigCode_
597:     )
598:         external
599:     {
600:         address account = msg.sender;
601:         if (isEth_) {
602:             LLocker.SLock memory oldLockData = _eLocker.getLockData(account, poolOwner_); // <= FOUND
603:             uint realDuration = duration_ + LLocker.restDuration(oldLockData); // <= FOUND
604:             if (realDuration > _maxDuration) { // <= FOUND
605:                 realDuration = _maxDuration; // <= FOUND
606:             } // <= FOUND
607:             uint maxEarning = _eEarning.maxEarningOf(account); // <= FOUND
608:             maxEarning -= amount_ * realDuration / _maxDuration; // <= FOUND
609:             _eEarning.updateMaxEarning(account, maxEarning); // <= FOUND
610:         }
611:          earningWithdraw(isEth_, amount_, payable(address(this)), 0); // <= FOUND
612:         _stake(isEth_, account, poolOwner_, duration_, minSPercent_, poolConfigCode_);
613:     }

['138']

138:     function claimFor(
139:         address account_,
140:         address dest_
141:     )
142:         external
143:         onlyAdmin
144:     {
145:         uint amount = rewardOf(account_);
146:         if (amount > 0) {
147:              // <= FOUND
148:             _setAdjReward(account_, amount, 0, _dToken.balanceOf(account_)); // <= FOUND
149:             _cashOut(dest_, amount); // <= FOUND
150:         }
151:     }

['270']

270:     function mintNewPosition(
271:         uint wethA_,
272:         uint wstethA_
273:     )
274:         internal
275:         returns
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1)
277:     {
278:         weth.approve(address(nonfungiblePositionManager), wethA_);
279:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
280: 
281:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth);
282:         address token1 = token0 == address(weth) ? address(wsteth) : address(weth);
283:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_;
284:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_;
285: 
286:         INonfungiblePositionManager.MintParams
287:             memory params = INonfungiblePositionManager.MintParams({ // <= FOUND
288:                 token0: token0, // <= FOUND
289:                 token1: token1, // <= FOUND
290:                 fee: POOL_FEE, // <= FOUND
291:                 tickLower: (MIN_TICK / TICK_SPACING) * TICK_SPACING, // <= FOUND
292:                 tickUpper: (MAX_TICK / TICK_SPACING) * TICK_SPACING, // <= FOUND
293:                 amount0Desired: amount0ToAdd, // <= FOUND
294:                 amount1Desired: amount1ToAdd, // <= FOUND
295:                 amount0Min: 0, // <= FOUND
296:                 amount1Min: 0, // <= FOUND
297:                 recipient: address(this), // <= FOUND
298:                 deadline: block.timestamp // <= FOUND
299:             }); // <= FOUND
300: 
301:         (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(
302:             params // <= FOUND
303:         );
304:     }

['306']

306:     function increaseLiquidityCurrentRange(
307:         uint tokenId_,
308:         uint wethA_,
309:         uint wstethA_
310:     ) internal returns (uint128 liquidity, uint amount0, uint amount1) {
311: 
312:         weth.approve(address(nonfungiblePositionManager), wethA_);
313:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
314: 
315:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth);
316:         
317:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_;
318:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_;
319: 
320:         INonfungiblePositionManager.IncreaseLiquidityParams
321:             memory params = INonfungiblePositionManager.IncreaseLiquidityParams({ // <= FOUND
322:                 tokenId: tokenId_, // <= FOUND
323:                 amount0Desired: amount0ToAdd, // <= FOUND
324:                 amount1Desired: amount1ToAdd, // <= FOUND
325:                 amount0Min: 0, // <= FOUND
326:                 amount1Min: 0, // <= FOUND
327:                 deadline: block.timestamp // <= FOUND
328:             }); // <= FOUND
329: 
330:         (liquidity, amount0, amount1) = nonfungiblePositionManager.increaseLiquidity(
331:             params // <= FOUND
332:         );
333:     }

['360']

360:     function decreaseLiquidityCurrentRange(
361:         uint tokenId_,
362:         uint128 decLiqA_
363:     )
364:         internal
365:         returns (uint amount0, uint amount1)
366:     {
367:         
368:         
369:         INonfungiblePositionManager.DecreaseLiquidityParams
370:             memory params = INonfungiblePositionManager.DecreaseLiquidityParams({ // <= FOUND
371:                 tokenId: tokenId_, // <= FOUND
372:                 liquidity: decLiqA_, // <= FOUND
373:                 amount0Min: 0, // <= FOUND
374:                 amount1Min: 0, // <= FOUND
375:                 deadline: block.timestamp // <= FOUND
376:             }); // <= FOUND
377: 
378:         (amount0, amount1) = nonfungiblePositionManager.decreaseLiquidity(params);
379:     }

['381']

381:     function swapExactInputSingleHop(
382:         address tokenIn,
383:         address tokenOut,
384:         uint amountIn
385:     )
386:         internal
387:         returns (uint amountOut) {
388:         ISwapRouter.ExactInputSingleParams memory params = ISwapRouter
389:             .ExactInputSingleParams({ // <= FOUND
390:                 tokenIn: tokenIn, // <= FOUND
391:                 tokenOut: tokenOut, // <= FOUND
392:                 fee: POOL_FEE, // <= FOUND
393:                 recipient: address(this), // <= FOUND
394:                  // <= FOUND
395:                 amountIn: amountIn, // <= FOUND
396:                 amountOutMinimum: 0, // <= FOUND
397:                 sqrtPriceLimitX96: 0 // <= FOUND
398:             }); // <= FOUND
399: 
400:         amountOut = router.exactInputSingle(params);
401:     }

[NonCritical-14] Consider implementing two-step procedure for updating protocol addresses

Resolution

Implementing a two-step procedure for updating protocol addresses adds an extra layer of security. In such a system, the first step initiates the change, and the second step, after a predefined delay, confirms and finalizes it. This delay allows stakeholders or monitoring tools to observe and react to unintended or malicious changes. If an unauthorized change is detected, corrective actions can be taken before the change is finalized. To achieve this, introduce a "proposed address" state variable and a "delay period". Upon an update request, set the "proposed address". After the delay, if not contested, the main protocol address can be updated.

Num of instances: 13

Findings

Click to show findings

['130']

130:     function updatePenaltyAddress( // <= FOUND
131:         address penaltyAddress_ // <= FOUND
132:     )
133:         external
134:         onlyOwner
135:     {
136:         _updatePenaltyAddress(penaltyAddress_);
137:     }

['66']

66:     function updateSponsor( // <= FOUND
67:         address account_, // <= FOUND
68:         address sponsor_ // <= FOUND
69:     )
70:         external
71:         onlyAdmin
72:     {
73:         _updateSponsor(account_, sponsor_);
74:     }

['144']

144:     function changeRewardPool( // <= FOUND
145:         address rewardPool_ // <= FOUND
146:     )
147:         external
148:         onlyAdmin
149:     {
150:         _rewardPool = rewardPool_;
151:     }

['146']

146:     function setInPrivateMode( // <= FOUND
147:         bool inPrivateMode_
148:     )
149:         external
150:         onlyOwner
151:     

['102']

102:     function setDefaultSPercentConfig( // <= FOUND
103:         uint sPercent_
104:     )
105:         external
106:         onlyAdmin
107:     

['112']

112:     function setMinSPercentConfig( // <= FOUND
113:         uint sPercent_
114:     )
115:         external
116:         onlyAdmin
117:     

['144']

144:     function changeRewardPool( // <= FOUND
145:         address rewardPool_
146:     )
147:         external
148:         onlyAdmin
149:     

['130']

130:     function updatePenaltyAddress( // <= FOUND
131:         address penaltyAddress_
132:     )
133:         external
134:         onlyOwner
135:     

['733']

733:     function updateConfigs( // <= FOUND
734:         uint[] memory values_
735:     )
736:         external
737:         onlyAdmin
738:     

['63']

63:     function updateMaxEarning( // <= FOUND
64:         address account_,
65:         uint maxEarning_
66:     )
67:         external
68:         onlyAdmin
69:     

['66']

66:     function updateSponsor( // <= FOUND
67:         address account_,
68:         address sponsor_
69:     )
70:         external
71:         onlyAdmin
72:     

['122']

122:     function updateFsOf( // <= FOUND
123:         address account_,
124:         uint fs_
125:     )
126:         external
127:         onlyAdmin
128:     

['134']

134:     function updateBoosterOf( // <= FOUND
135:         address account_,
136:         uint booster_
137:     )
138:         external
139:         onlyAdmin
140:     

[NonCritical-15] Library has public/external functions

Resolution

Libraries in Solidity are meant to be static collections of functions. They are deployed once and their code is reused by contracts through DELEGATECALL, which executes the library's code in the context of the calling contract. Public or external functions in libraries can be misleading because libraries are not supposed to maintain state or have external interactions in the way contracts do. Designing libraries with these kinds of functions goes against their intended purpose and can lead to confusion or misuse. For state management or external interactions, using contracts instead of libraries is more appropriate. Libraries should focus on utility functions that can operate on the data passed to them without side effects, enhancing code reuse and gas efficiency.

Num of instances: 1

Findings

Click to show findings

['135']

135: library LLido { // <= FOUND
136:     ISwapRouter private constant router =
137:         ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
138:     IWETH internal constant weth =
139:         IWETH(0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6);
140: 
143:     IERC20 internal constant wsteth =
144:         IERC20(0x0D47dF42B5d503EcC6A366499B2B97c7D5Ad42eE);
145: 
146:     uint24 private constant POOL_FEE = 100;
147: 
148:     int24 private constant MIN_TICK = -887272;
149:     int24 private constant MAX_TICK = -MIN_TICK;
150:     int24 private constant TICK_SPACING = 60;
151: 
152:     INonfungiblePositionManager private constant nonfungiblePositionManager =
153:         INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
154: 
155:     function createPairForTestnet(
156:         address wstethAddr_
157:     )
158:         internal
159:     {
160:         IUniswapV3Factory factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
161:         factory.createPool(address(weth), wstethAddr_, POOL_FEE);
162:     }
163: 
164:     function add(
165:         uint tokenId_
166:     )
167:         internal
168:         returns(uint128 liquidity)
169:     {
170:         uint amount = weth.balanceOf(address(this));
171:         uint wethA = amount / 2;
172:         buyWsteth(amount - wethA);
173:         uint wstethA = wsteth.balanceOf(address(this));
174:         (liquidity, , ) = increaseLiquidityCurrentRange(tokenId_, wethA, wstethA);
175:     }
176: 
177:     function remove(
178:         uint tokenId_,
179:         uint128 decLiqA_
180:     )
181:         internal
182:     {
183:         decreaseLiquidityCurrentRange(tokenId_, decLiqA_);
184:         collectAllFees(tokenId_);
185:         uint wstethA = wsteth.balanceOf(address(this));
186:         sellWsteth(wstethA);
187:     }
188: 
189:     function mint()
190:         internal
191:         returns(uint tokenId, uint128 liquidity)
192:     {
193:         uint amount = weth.balanceOf(address(this));
194:         uint wethA = amount / 2;
195:         buyWsteth(amount - wethA);
196:         uint wstethA = wsteth.balanceOf(address(this));
197:         (tokenId, liquidity, , ) = mintNewPosition(wethA, wstethA);
198:     }
199: 
200:     function buyWsteth( // <= FOUND
201:         uint wethA_
202:     )
203:         public
204:     {
205:         if (wethA_ == 0) {
206:             return;
207:         }
208:         weth.approve(address(router), wethA_);
209:         swapExactInputSingleHop(
210:             address(weth),
211:             address(wsteth),
212:             wethA_
213:         );
214:     }
215: 
216:     function allToWsteth(
217:         uint minWstethBal_
218:     )
219:         public
220:     {
221:         ethToWeth();
222:         buyWsteth(weth.balanceOf(address(this)));
223:         require(wsteth.balanceOf(address(this)) > minWstethBal_, "slipped too high");
224:     }
225: 
226:     function allToEth(
227:         uint minEthBal_
228:     )
229:         public
230:     {
231:         sellWsteth(wsteth.balanceOf(address(this)));
232:         wethToEth();
233:         require(address(this).balance > minEthBal_, "slipped too high");
234:     }
235: 
236:     function sellWsteth(
237:         uint wstethA_
238:     )
239:         public
240:     {
241:         if (wstethA_ == 0) {
242:             return;
243:         }
244:         wsteth.approve(address(router), wstethA_);
245:         swapExactInputSingleHop(
246:             address(wsteth),
247:             address(weth),
248:             wstethA_
249:         );
250:     }
251: 

[NonCritical-16] Interfaces should be declared in a separate file

Resolution

It is general standard to declare interfaces on files separate from regular contract declarations

Num of instances: 1

Findings

Click to show findings

['7']

7: interface INonfungiblePositionManager  // <= FOUND

[NonCritical-17] Events regarding state variable changes should emit the previous state variable value

Resolution

Modify such events to contain the previous value of the state variable as demonstrated in the example below

Num of instances: 12

Findings

Click to show findings

['9']

9: event SetCleanTo(
10:         address indexed cleanTo
11:     );

['48']

48: event SetInPrivateMode(
49:         bool inPrivateMode
50:     );

['21']

21: event SetSPercent(
22:         address indexed account,
23:         uint sPercent
24:     );

['46']

46: event AdminUpdateConfig(
47:         uint[] values
48:     );

['14']

14: event UpdateMaxEarning(
15:         address indexed account,
16:         uint maxEarning
17:     );

['16']

16: event UpdateLockData(
17:         address indexed account,
18:         address indexed poolOwner,
19:         LLocker.SLock lockData
20:     );

['33']

33: event UpdatePenaltyAddress(
34:         address penaltyAddress
35:     );

['11']

11: event UpdateAthBalance(
12:         address indexed account_,
13:         uint athBalance
14:     );

['15']

15: event UpdateSponsor(
16:         address indexed account,
17:         address indexed sponsor,
18:         uint sPercent
19:     );

['26']

26: event UpdateFsOf(
27:         address indexed account,
28:         uint fs
29:     );

['31']

31: event UpdateBoosterOf(
32:         address indexed account,
33:         uint booster
34:     );

['16']

16: event UpdateLockData(
17:         address indexed account,
18:         LLocker.SLock lockData
19:     );

[NonCritical-18] In functions which accept an address as a parameter, there should be a zero address check to prevent bugs

Resolution

In smart contract development, especially with Solidity, it's crucial to validate inputs to functions. When a function accepts an Ethereum address as a parameter, implementing a zero address check (i.e., ensuring the address is not 0x0) is a best practice to prevent potential bugs and vulnerabilities. The zero address (0x0) is a default value and generally indicates an uninitialized or invalid state. Passing the zero address to certain functions can lead to unintended behaviors, like funds getting locked permanently or transactions failing silently. By checking for and rejecting the zero address, developers can ensure that the function operates as intended and interacts only with valid Ethereum addresses. This check enhances the contract's robustness and security.

Num of instances: 114

Findings

Click to show findings

['27']

27:     function _addAdmin(
28:         address account_
29:     )
30:         internal
31:     

['38']

38:     function _removeAdmin(
39:         address account_
40:     )
41:         internal
42:     

['52']

52:     function addAdmins(
53:         address[] memory accounts_
54:     )
55:         external
56:         onlyOwner
57:     

['65']

65:     function removeAdmins(
66:         address[] memory accounts_
67:     )
68:         external
69:         onlyOwner
70:     

['95']

95:     function isOwner(
96:         address account_
97:     )
98:         external
99:         view
100:         returns(bool)
101:     

['104']

104:     function isAdmin(
105:         address account_
106:     )
107:         external
108:         view
109:         returns(bool)
110:     

['40']

40:     function init(
41:         address dToken_,
42:         address rewardToken_
43:     )
44:         public
45:         initializer
46:     

['69']

69:     function claimFor(
70:         address holder_
71:     )
72:         public
73:     

['110']

110:     function claimableOf(
111:         address holder_
112:     )
113:         public
114:         view
115:         returns(uint reward)
116:     

['25']

25:     function _initCashier(
26:         address token_,
27:         address cleanTo_
28:     )
29:         internal
30:     

['35']

35:     function _setCleanTo(
36:         address cleanTo_
37:     )
38:         internal
39:     

['60']

60:     function _cashOut(
61:         address to_,
62:         uint amount_
63:     )
64:         internal
65:     

['108']

108:     function initController(
109:         address[] memory contracts_
110:     )
111:         external
112:         initializer
113:     

['147']

147:     function _reCalEP2PDBalance(
148:         address poolOwner_
149:     )
150:         internal
151:     

['172']

172:     function _reCalFs(
173:         address account_
174:     )
175:         internal
176:     

['186']

186:     function _reCalBooster(
187:         address account_
188:     )
189:         internal
190:     

['198']

198:     function _updateSponsor(
199:         address payable poolOwner_,
200:         address staker_,
201:         uint minSPercent_
202:     )
203:         internal
204:     

['243']

243:     function _sharePoolOwner(
244:         uint amount_,
245:         address payable poolOwner_
246:     )
247:         internal
248:     

['257']

257:     function _sharePoolUser(
258:         uint amount_,
259:         address payable poolOwner_
260:     )
261:         internal
262:     

['267']

267:     function _lock(
268:         bool isEth_,
269:         address payable poolOwner_,
270:         uint duration_
271:     )
272:         internal
273:         returns (uint)
274:     

['293']

293:     function _stake(
294:         bool isEth_,
295:         address account_,
296:         address payable poolOwner_,
297:         uint duration_,
298:         uint minSPercent_,
299:         uint poolConfigCode_
300:     )
301:         internal
302:     

['412']

412:     function ethStake(
413:         address payable poolOwner_,
414:         uint duration_,
415:         uint minSPercent_,
416:         uint poolConfigCode_,
417:         uint minWstethA_,
418:         uint wstethA_
419:     )
420:         public
421:         payable
422:         tryPublicMint
423:     

['429']

429:     function dctStake(
430:         uint amount_,
431:         address payable poolOwner_,
432:         uint duration_
433:     )
434:         public
435:         payable
436:         tryPublicMint
437:     

['447']

447:     function _distributorClaimFor(
448:         address distributor_,
449:         address account_,
450:         address dest_
451:     )
452:         internal
453:     

['458']

458:     function _earningPull(
459:         address account_,
460:         address poolOwner_
461:     )
462:         internal
463:     

['478']

478:     function earningPulls(
479:         address account_,
480:         address[] memory poolOwners_,
481:         address bountyPullerTo_
482:     )
483:         public
484:         tryPublicMint
485:     

['543']

543:     function lockWithdraw(
544:         bool isEth_,
545:         address payable poolOwner_,
546:         uint amount_,
547:         address payable dest_,
548:         bool isForced_,
549:         uint minEthA_
550:     )
551:         public
552:         tryPublicMint
553:     

['589']

589:     function earningReinvest(
590:         bool isEth_,
591:         address payable poolOwner_,
592:         uint duration_,
593:         uint amount_,
594:         
595:         uint minSPercent_,
596:         uint poolConfigCode_
597:     )
598:         external
599:     

['615']

615:     function earningWithdraw(
616:         bool isEth_,
617:         uint amount_,
618:         address payable dest_,
619:         uint minEthA_
620:         
621:     )
622:         public
623:     

['641']

641:     function createVote(
642:         address defender_,
643:         uint dEthValue_,
644:         uint voterPercent_,
645:         uint freezeDuration_,
646:         uint minWstethA_,
647:         uint wstethA_
648:     )
649:         external
650:         payable
651:     

['693']

693:     function votingClaimFor(
694:         uint voteId_,
695:         address voter_
696:     )
697:         external
698:     

['27']

27:     function initDCT(
28:         address accessControl_,
29:         address rewardPool_,
30:         address premineAddress_,
31:         uint256 premineAmount_,
32:         address cleanTo_
33:     )
34:         external
35:         initializer
36:     

['55']

55:     function _beforeTokenTransfer(
56:         address from_,
57:         address,
58:         uint256
59:     )
60:         internal
61:         virtual
62:         override
63:     

['140']

140:     function balanceOf(address account_) public view override returns (uint256) 

['144']

144:     function changeRewardPool(
145:         address rewardPool_
146:     )
147:         external
148:         onlyAdmin
149:     

['14']

14:     function initDToken(
15:         address accessControl_,
16:         bool inPrivateMode_,
17:         string memory name_,
18:         string memory symbol_,
19:         address[] memory distributorAddrs_
20:     )
21:         public
22:         initializer
23:     

['34']

34:     function _beforeTokenTransfer(
35:         address from_,
36:         address to_,
37:         uint256 amount_
38:     )
39:         internal
40:         virtual
41:         override
42:     

['34']

34:     function initDistributor(
35:         address accessControl_,
36:         address dToken_,
37:         address rewardToken_
38:     )
39:         public
40:         initializer
41:     

['96']

96:     function _setAdjReward(
97:         address account_,
98:         uint prevReward,
99:         uint reward_,
100:         uint dTokenBalance_
101:     )
102:         internal
103:     

['128']

128:     function rewardOf(
129:         address account_
130:     )
131:         public
132:         view
133:         returns(uint)
134:     

['138']

138:     function claimFor(
139:         address account_,
140:         address dest_
141:     )
142:         external
143:         onlyAdmin
144:     

['35']

35:     function initEarning(
36:         address token_,
37:         address profileCAddr_,
38:         address accessControl_,
39:         string memory name_,
40:         string memory symbol_,
41:         address cleanTo_
42:     )
43:         public
44:         virtual
45:         initializer
46:     

['53']

53:     function _updateMaxEarning(
54:         address account_,
55:         uint maxEarning_
56:     )
57:         internal
58:     

['63']

63:     function updateMaxEarning(
64:         address account_,
65:         uint maxEarning_
66:     )
67:         external
68:         onlyAdmin
69:     

['73']

73:     function shareCommission(
74:         address account_
75:     )
76:         public
77:     

['28']

28:     function update(
29:         address account_,
30:         bool needShareComm_
31:     )
32:         external
33:     

['117']

117:     function withdraw(
118:         address account_,
119:         uint amount_,
120:         address dest_
121:     )
122:         external
123:         onlyAdmin
124:     

['48']

48:     function maxEarningOf(
49:         address account_
50:     )
51:         external
52:         view
53:         returns(uint)
54:     

['41']

41:     function earningOf(
42:         address account_
43:     )
44:         external
45:         view
46:         returns(uint)
47:     

['39']

39:     function initEthSharing(
40:         address accessControl_,
41:         uint devTeamPercent_,
42:         uint defaultOwnerPercent_,
43:         uint defaultUserPercent_,
44:         bool inDefaultOnlyMode_
45: 
46:     )
47:         external
48:         initializer
49:     

['70']

70:     function getPoolCode(
71:         address poolOwner_
72:     )
73:         public
74:         view
75:         returns(uint)
76:     

['81']

81:     function _configPool(
82:         address poolOwner_,
83:         uint ownerPercent_,
84:         uint userPercent_
85:     )
86:         internal
87:     

['139']

139:     function tryResetPool(
140:         address poolOwner_
141:     )
142:         external
143:         onlyAdmin
144:     

['150']

150:     function initPoolConfig(
151:         address poolOwner_
152:     )
153:         external
154:         onlyAdmin
155:         returns(bool)
156:     

['175']

175:     function getPoolLockPercent(
176:         address poolOwner_
177:     )
178:         public
179:         view
180:         returns(uint)
181:     

['208']

208:     function getPoolOwnerPart(
209:         address poolOwner_,
210:         uint value_
211:     )
212:         public
213:         view
214:         returns(uint)
215:     

['222']

222:     function getPoolUserPart(
223:         address poolOwner_,
224:         uint value_
225:     )
226:         public
227:         view
228:         returns(uint)
229:     

['236']

236:     function getLockedPart(
237:         address poolOwner_,
238:         uint value_
239:     )
240:         public
241:         view
242:         returns(uint)
243:     

['251']

251:     function getSharingParts(
252:         address poolOwner_,
253:         uint value_,
254:         uint code_
255:     )
256:         public
257:         view
258:         returns(uint devTeamA, uint poolOwnerA, uint poolUserA, uint lockedA)
259:     

['155']

155:     function createPairForTestnet(
156:         address wstethAddr_
157:     )
158:         internal
159:     

['381']

381:     function swapExactInputSingleHop(
382:         address tokenIn,
383:         address tokenOut,
384:         uint amountIn
385:     )
386:         internal
387:         returns (uint amountOut) 

['14']

14:     function getLockId(
15:         address account_,
16:         address poolOwner_
17:     )
18:         internal
19:         pure
20:         returns(bytes32)
21:     

['42']

42:     function initLocker(
43:         address accessControl_,
44:         address token_,
45:         address profileCAddr_,
46:         address penaltyAddress_,
47:         address cleanTo_
48:     )
49:         public
50:         initializer
51:     

['59']

59:     function lock(
60:         address account_,
61:         address poolOwner_,
62:         uint duration_
63:     )
64:         external
65:         onlyAdmin
66:     

['74']

74:     function withdraw(
75:         address account_,
76:         address poolOwner_,
77:         address dest_,
78:         uint amount_,
79:         bool isForced_
80:     )
81:         external
82:         onlyApprovedAdmin(account_)
83:     

['87']

87:     function _withdraw(
88:         address account_,
89:         address poolOwner_,
90:         address dest_,
91:         uint amount_,
92:         bool isForced_
93:     )
94:         internal
95:     

['121']

121:     function _updatePenaltyAddress(
122:         address penaltyAddress_
123:     )
124:         internal
125:     

['130']

130:     function updatePenaltyAddress(
131:         address penaltyAddress_
132:     )
133:         external
134:         onlyOwner
135:     

['38']

38:     function getLockId(
39:         address account_,
40:         address poolOwner_
41:     )
42:         external
43:         pure
44:         returns(bytes32)
45:     

['53']

53:     function getLockData(
54:         address account_,
55:         address poolOwner_
56:     )
57:         external
58:         view
59:         returns(LLocker.SLock memory)
60:     

['134']

134:     function isContract(address _addr) private view returns (bool)

['26']

26:     function _updateAthBalance(
27:         address account_,
28:         uint balance_
29:     )
30:         internal
31:     

['58']

58:     function initPERC20(
59:         address accessControl_,
60:         bool inPrivateMode_,
61:         string memory name_,
62:         string memory symbol_
63:     )
64:         public
65:         virtual
66:         initializer
67:     

['101']

101:     function _afterTokenTransfer(
102:         address,
103:         address to_,
104:         uint256
105:     )
106:         internal
107:         virtual
108:         override
109:     

['136']

136:     function transfer(address to, uint256 amount) public virtual override returns (bool) 

['141']

141:     function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) 

['155']

155:     function mint(
156:         address account_,
157:         uint amount_
158:     )
159:         external
160:         onlyAdmin
161:     

['165']

165:     function burn(
166:         address account_,
167:         uint amount_
168:     )
169:         external
170:         virtual
171:         onlyAdmin
172:     

['35']

35:     function initPoolFactory(
36:         address accessControl_,
37:         address geth_,
38:         address dct_
39:     )
40:         external
41:         initializer
42:     

['65']

65:     function _createPool(
66:         address owner_
67:     )
68:         internal
69:     

['104']

104:     function createPool(
105:         address owner_
106:     )
107:         external
108:         onlyAdmin
109:     

['114']

114:     function isCreated(
115:         address owner_
116:     )
117:         external
118:         view
119:         returns(bool)
120:     

['124']

124:     function getPool(
125:         address owner_
126:     )
127:         external
128:         view
129:         returns(SPool memory)
130:     

['134']

134:     function isContract(address _addr) private view returns (bool)

['25']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     

['41']

41:     function transferLock(
42:         address from_,
43:         address to_,
44:         uint amount_
45:     )
46:         external
47:         onlyOwner
48:     

['63']

63:     function withdraw(
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     

['73']

73:     function sendTos(
74:         address[] memory investers_,
75:         uint[] memory amounts_,
76:         uint[] memory vestingDurs_,
77:         uint[] memory vestingPercents_,
78:         uint[] memory cliffs_
79:     )
80:         external
81:         onlyOwner
82:     

['40']

40:     function initProfile(
41:         address accessControl_
42:     )
43:         external
44:         initializer
45:     

['66']

66:     function updateSponsor(
67:         address account_,
68:         address sponsor_
69:     )
70:         external
71:         onlyAdmin
72:     

['92']

92:     function _initDefaultSPercent(
93:         address account_
94:     )
95:         internal
96:     

['122']

122:     function updateFsOf(
123:         address account_,
124:         uint fs_
125:     )
126:         external
127:         onlyAdmin
128:     

['134']

134:     function updateBoosterOf(
135:         address account_,
136:         uint booster_
137:     )
138:         external
139:         onlyAdmin
140:     

['20']

20:     function profileOf(
21:         address account_
22:     )
23:         external
24:         view
25:         returns(SProfile memory)
26:     

['62']

62:     function fsOf(
63:         address account_
64:     )
65:         external
66:         view
67:         returns(uint)
68:     

['69']

69:     function boosterOf(
70:         address account_
71:     )
72:         external
73:         view
74:         returns(uint)
75:     

['73']

73:     function initUseAccessControl(
74:         address accessControl_
75:     )
76:         public
77:         initializer
78:     

['83']

83:     function approveAdmin(
84:         address admin_
85:     )
86:         external
87:     

['95']

95:     function revokeAdmin(
96:         address admin_
97:     )
98:         external
99:     

['107']

107:     function isApprovedAdmin(
108:         address account_,
109:         address admin_
110:     )
111:         external
112:         view
113:         returns(bool)
114:     

['23']

23:     function initVester(
24:         address accessControl_,
25:         address token_,
26:         address cleanTo_
27:     )
28:         public
29:         initializer
30:     

['35']

35:     function lock(
36:         address account_,
37:         uint duration_,
38:         uint cliff_
39:     )
40:         external
41:         onlyAdmin
42:     

['52']

52:     function transferLock(
53:         address from_,
54:         address to_,
55:         uint amount_
56:     )
57:         external
58:         onlyAdmin
59:     

['75']

75:     function unlock(
76:         address account_
77:     )
78:         public
79:     

['109']

109:     function currentLockData(
110:         address account_
111:     )
112:         public
113:         view
114:         returns(uint restA, uint restDuration)
115:     

['123']

123:     function getUnlockedA(
124:         address account_
125:     )
126:         external
127:         view
128:         returns(uint)
129:     

['135']

135:     function getLockData(
136:         address account_
137:     )
138:         external
139:         view
140:         returns(LLocker.SLock memory)
141:     

['119']

119:     function initVoting(
120:         address accessControl_,
121:         address votingTokenAddr_,
122:         address gethAddr_,
123:         address eEarningAddr_,
124:         address cleanTo_
125:     )
126:         external
127:         initializer
128:     

['136']

136:     function createVote(
137:         address attacker_,
138:         address defender_,
139: 
140:         uint aEthValue_,
141:         uint dEthValue_,
142: 
143:         uint voterPercent_,
144:         uint aQuorum_,
145: 
146:         uint startedAt_,
147:         uint endAt_
148:     )
149:         external
150:         onlyAdmin
151:     

['191']

191:     function _removeVoter(
192:         uint voteId_,
193:         address voter_
194:     )
195:         internal
196:     

['210']

210:     function _addVoter(
211:         uint voteId_,
212:         address voter_,
213:         bool isForAttacker_
214:     )
215:         internal
216:     

['297']

297:     function claimFor(
298:         uint voteId_,
299:         address voter_
300:     )
301:         external
302:         onlyAdmin
303:     

['327']

327:     function closeVote(
328:         uint voteId_,
329:         address dest_
330:     )
331:         external
332:         onlyAdmin
333:     

['373']

373:     function defenderEarningFreezedOf(
374:         address account_
375:     )
376:         external
377:         view
378:         returns(uint)
379:     

[NonCritical-19] Enum values should be used in place of constant array indexes

Resolution

Create a commented enum value to use in place of constant array indexes, this makes the code far easier to understand

Num of instances: 24

Findings

Click to show findings

['114']

114:         _geth = IERC20(contracts_[0]); // <= FOUND

['227']

227:             pools[0] = poolOwner_; // <= FOUND

['740']

740:         require(values_[0] <= 300, "max 3%"); // <= FOUND

['741']

741:         _bountyPullEarningPercent = values_[0]; // <= FOUND

['80']

80:         poolDistributorAddrs[0] = pool.ethDistributor; // <= FOUND

['115']

115:         _dct = IDCT(contracts_[1]); // <= FOUND

['743']

743:         _maxBooster = values_[1]; // <= FOUND

['81']

81:         poolDistributorAddrs[1] = pool.dctDistributor; // <= FOUND

['117']

117:         initUseAccessControl(contracts_[2]); // <= FOUND

['745']

745:         _maxSponsorAdv = values_[2]; // <= FOUND

['118']

118:         _devTeam = IDistributor(contracts_[3]); // <= FOUND

['746']

746:         _maxSponsorAfter = values_[3]; // <= FOUND

['119']

119:         _poolFactory = IPoolFactory(contracts_[4]); // <= FOUND

['748']

748:         _attackFee = values_[4]; // <= FOUND

['120']

120:         _profileC = IProfile(contracts_[5]); // <= FOUND

['749']

749:         _maxVoterPercent = values_[5]; // <= FOUND

['122']

122:         _eLocker = ILocker(contracts_[6]); // <= FOUND

['750']

750:         _minAttackerFundRate = values_[6]; // <= FOUND

['123']

123:         _eP2PDToken = IDToken(contracts_[7]); // <= FOUND

['751']

751:         _freezeDurationUnit = values_[7]; // <= FOUND

['125']

125:         
126:         _eEarning = IEarning(contracts_[8]); // <= FOUND

['752']

752:         _selfStakeAdvantage = values_[8]; // <= FOUND

['127']

127:         _dLocker = ILocker(contracts_[9]); // <= FOUND

['754']

754:         _profileC.setDefaultSPercentConfig(values_[9]); // <= FOUND

[NonCritical-20] Default bool values are manually set

Resolution

In instances where a new variable is defined, there is no need to set it to it's default value.

Num of instances: 2

Findings

Click to show findings

['441']

441:         bool isEth = false; // <= FOUND

['23']

23:     bool public isMintingFinished = false; // <= FOUND

[NonCritical-21] Revert statements within external and public functions can be used to perform DOS attacks

Resolution

In Solidity, 'revert' statements are used to undo changes and throw an exception when certain conditions are not met. However, in public and external functions, improper use of revert can be exploited for Denial of Service (DoS) attacks. An attacker can intentionally trigger these 'revert' conditions, causing legitimate transactions to consistently fail. For example, if a function relies on specific conditions from user input or contract state, an attacker could manipulate these to continually force reverts, blocking the function's execution. Therefore, it's crucial to design contract logic to handle exceptions properly and avoid scenarios where revert can be predictably triggered by malicious actors. This includes careful input validation and considering alternative design patterns that are less susceptible to such abuses.

Num of instances: 1

Findings

Click to show findings

['53']

47:     function clean()
48:         public
49:         pure
50:         override
51:     {
52:         
53:         revert(); // <= FOUND
54:     }

[NonCritical-22] Functions which are either private or internal should have a preceding _ in their name

Resolution

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

Num of instances: 28

Findings

Click to show findings

['11']

11:     function calEP2PDBalance(
12:         uint fs_,
13:         uint booster_,
14:         uint totalP2UBalance_
15:     )
16:         internal
17:         pure
18:         returns(uint)
19:     

['23']

23:     function calMintStakingPower(
24:         LLocker.SLock memory oldLockData,
25:         uint lockAmount_,
26:         uint lockTime_,
27:         bool isSelfStake_,
28:         uint selfStakeAdvantage_
29:     )
30:         internal
31:         view
32:         returns(uint)
33:     

['49']

49:     function calBurnStakingPower(
50:         uint powerBalance_,
51:         uint unlockedA_,
52:         uint totalLockedA_
53:     )
54:         internal
55:         pure
56:         returns(uint)
57:     

['61']

61:     function calFs(
62:         uint earningBalance_,
63:         uint maxEarning_
64:     )
65:         internal
66:         pure
67:         returns(uint)
68:     

['79']

79:     function calMultiplierForOldAmount(
80:         uint lockTime_
81:     )
82:         internal
83:         pure
84:         returns(uint)
85:     

['91']

91:     function calMultiplier(
92:         uint lockTime_
93:     )
94:         internal
95:         pure
96:         returns(uint)
97:     

['103']

103:     function calAQuorum(
104:         uint aEthValue_,
105:         uint dEthValue_,
106:         uint voterPercent_,
107:         uint freezeDuration_,
108:         uint freezeDurationUnit_
109:     )
110:         internal
111:         pure
112:         returns(uint)
113:     

['155']

155:     function createPairForTestnet(
156:         address wstethAddr_
157:     )
158:         internal
159:     

['164']

164:     function add(
165:         uint tokenId_
166:     )
167:         internal
168:         returns(uint128 liquidity)
169:     

['177']

177:     function remove(
178:         uint tokenId_,
179:         uint128 decLiqA_
180:     )
181:         internal
182:     

['189']

189:     function mint()
190:         internal
191:         returns(uint tokenId, uint128 liquidity)
192:     

['270']

270:     function mintNewPosition(
271:         uint wethA_,
272:         uint wstethA_
273:     )
274:         internal
275:         returns
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1)
277:     

['306']

306:     function increaseLiquidityCurrentRange(
307:         uint tokenId_,
308:         uint wethA_,
309:         uint wstethA_
310:     ) internal returns (uint128 liquidity, uint amount0, uint amount1) 

['335']

335:     function collectAllFees(
336:         uint tokenId_
337:     ) internal returns (uint amount0, uint amount1) 

['349']

349:     function getLiquidity(
350:         uint tokenId_
351:     )
352:         internal
353:         view
354:         returns (uint128)
355:     

['360']

360:     function decreaseLiquidityCurrentRange(
361:         uint tokenId_,
362:         uint128 decLiqA_
363:     )
364:         internal
365:         returns (uint amount0, uint amount1)
366:     

['381']

381:     function swapExactInputSingleHop(
382:         address tokenIn,
383:         address tokenOut,
384:         uint amountIn
385:     )
386:         internal
387:         returns (uint amountOut) 

['14']

14:     function getLockId(
15:         address account_,
16:         address poolOwner_
17:     )
18:         internal
19:         pure
20:         returns(bytes32)
21:     

['25']

25:     function restDuration(
26:         SLock memory lockData_
27:     )
28:         internal
29:         view
30:         returns(uint)
31:     

['43']

43:     function prolong(
44:         SLock storage lockData_,
45:         uint amount_,
46:         uint duration_
47:     )
48:         internal
49:     

['68']

68:     function isUnlocked(
69:         SLock memory lockData_,
70:         uint fs_,
71:         bool isPoolOwner_
72:     )
73:         internal
74:         view
75:         returns(bool)
76:     

['83']

83:     function calDuration(
84:         SLock memory lockData_,
85:         uint fs_,
86:         bool isPoolOwner_
87:     )
88:         internal
89:         pure
90:         returns(uint)
91:     

['10']

10:     function validatePercent(
11:         uint percent_
12:     )
13:         internal
14:         pure
15:     

['20']

20:     function getPercentA(
21:         uint value,
22:         uint percent
23:     )
24:         internal
25:         pure
26:         returns(uint)
27:     

['12']

12:     function invertOf(
13:         uint value_
14:     )
15:         internal
16:         pure
17:         returns(uint)
18:     

['22']

22:     function calBooster(
23:         uint boostVotePower_,
24:         uint maxBoostVotePower_,
25:         uint maxBooster_
26:     )
27:         pure
28:         internal
29:         returns(uint)
30:     

['134']

134:     function isContract(address _addr) private view returns (bool)

['134']

134:     function isContract(address _addr) private view returns (bool)

[NonCritical-23] Public state variables shouldn't have a preceding _ in their name

Resolution

Remove the _ from the state variable name, ensure you also refactor where these state variables are internally called

Num of instances: 22

Findings

Click to show findings

['76']

76: uint public _maxBooster = 2; // <= FOUND

['78']

78: uint public _minDuration = 30 days; // <= FOUND

['79']

79: uint public _maxDuration = 720 days; // <= FOUND

['80']

80: uint public _minStakeETHAmount = 0.001e18; // <= FOUND

['81']

81: uint public _minStakeDCTAmount = 7 ether; // <= FOUND

['83']

83: uint public _maxSponsorAdv = 7; // <= FOUND

['84']

84: uint public _maxSponsorAfter = 7 days; // <= FOUND

['86']

86: uint public _lastMintAt; // <= FOUND

['87']

87: uint public _mintingInt = 7; // <= FOUND

['89']

89: uint public _attackFee = 1 ether; // <= FOUND

['90']

90: uint public _minFreezeDuration = 1 days; // <= FOUND

['91']

91: uint public _maxFreezeDuration = 7 days; // <= FOUND

['92']

92: uint public _freezeDurationUnit = 7 days; // <= FOUND

['93']

93: uint public _minDefenderFund = 0.001e18; // <= FOUND

['95']

95: uint public _maxVoterPercent = 5000;  // <= FOUND

['96']

96: uint public _minAttackerFundRate = 2500;  // <= FOUND

['98']

98: uint public _bountyPullEarningPercent = 100;  // <= FOUND

['100']

100: uint public _selfStakeAdvantage = 15000;  // <= FOUND

['102']

102: uint public _isPausedAttack = 0; // <= FOUND

['104']

104: uint public _dctTaxPercent = 100; // <= FOUND

['37']

37: uint public _defaultSPercent = 1000;  // <= FOUND

['38']

38: uint public _minSPercent = 1000;  // <= FOUND

[NonCritical-24] Contract lines should not be longer than 120 characters for readability

Resolution

Consider spreading these lines over multiple lines to aid in readability and the support of VIM users everywhere.

Num of instances: 5

Findings

Click to show findings

['41']

41:         uint rs = (oldALock * calMultiplierForOldAmount(dLockForOldA) + lockAmount_ * calMultiplier(dLockForStakeA)) / LPercentage.DEMI; // <= FOUND

['579']

579:             uint burnedPower = LHelper.calBurnStakingPower(_dP2PDToken.balanceOf(poolOwner_), amount_, oldLockData.amount); // <= FOUND

['664']

664:         require(freezeDuration_ >= _minFreezeDuration && freezeDuration_ <= _maxFreezeDuration, "freezeDuration_ invalid"); // <= FOUND

['665']

665:         require(aEthValue <= dEthValue_ && aEthValue * LPercentage.DEMI / dEthValue_ >= _minAttackerFundRate, "aEthValue invalid"); // <= FOUND

['33']

33: //     IBlastPoints(0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800).configurePointsOperator(0x6d9cD20Ba0Dc1CCE0C645a6b5759f5ad1bD2704F); // <= FOUND

[NonCritical-25] Avoid updating storage when the value hasn't changed

Resolution

In Solidity, manipulating contract storage comes with significant gas costs. One can optimize gas usage by preventing unnecessary storage updates when the new value is the same as the existing one. If an existing value is the same as the new one, not reassigning it to the storage could potentially save substantial amounts of gas, notably 2900 gas for a 'Gsreset'. This saving may come at the expense of a cold storage load operation ('Gcoldsload'), which costs 2100 gas, or a warm storage access operation ('Gwarmaccess'), which costs 100 gas. Therefore, the gas efficiency of your contract can be significantly improved by adding a check that compares the new value with the current one before any storage update operation. If the values are the same, you can bypass the storage operation, thereby saving gas.

Num of instances: 10

Findings

Click to show findings

['146']

146:     function setInPrivateMode(
147:         bool inPrivateMode_
148:     )
149:         external
150:         onlyOwner
151:     {
152:         _setInPrivateMode(inPrivateMode_);
153:     }

['102']

102:     function setDefaultSPercentConfig(
103:         uint sPercent_
104:     )
105:         external
106:         onlyAdmin
107:     {
108:         LPercentage.validatePercent(sPercent_);
109:         _defaultSPercent = sPercent_;
110:     }

['112']

112:     function setMinSPercentConfig(
113:         uint sPercent_
114:     )
115:         external
116:         onlyAdmin
117:     {
118:         LPercentage.validatePercent(sPercent_);
119:         _minSPercent = sPercent_;
120:     }

['144']

144:     function changeRewardPool(
145:         address rewardPool_
146:     )
147:         external
148:         onlyAdmin
149:     {
150:         _rewardPool = rewardPool_;
151:     }

['130']

130:     function updatePenaltyAddress(
131:         address penaltyAddress_
132:     )
133:         external
134:         onlyOwner
135:     {
136:         _updatePenaltyAddress(penaltyAddress_);
137:     }

['733']

733:     function updateConfigs(
734:         uint[] memory values_
735:     )
736:         external
737:         onlyAdmin
738:     {
739: 
740:         require(values_[0] <= 300, "max 3%");
741:         _bountyPullEarningPercent = values_[0];
742: 
743:         _maxBooster = values_[1];
744: 
745:         _maxSponsorAdv = values_[2];
746:         _maxSponsorAfter = values_[3];
747: 
748:         _attackFee = values_[4];
749:         _maxVoterPercent = values_[5];
750:         _minAttackerFundRate = values_[6];
751:         _freezeDurationUnit = values_[7];
752:         _selfStakeAdvantage = values_[8];
753: 
754:         _profileC.setDefaultSPercentConfig(values_[9]);
755:         _isPausedAttack = values_[10];
756: 
757:         _profileC.setMinSPercentConfig(values_[11]);
758: 
759:         _dctTaxPercent = values_[12];
760: 
761:         _minFreezeDuration = values_[13];
762:         _maxFreezeDuration = values_[14];
763: 
764:         _minStakeETHAmount = values_[15];
765:         _minStakeDCTAmount = values_[16];
766: 
767:         _minDefenderFund = values_[17];
768: 
769:         emit AdminUpdateConfig(values_);
770:     }

['63']

63:     function updateMaxEarning(
64:         address account_,
65:         uint maxEarning_
66:     )
67:         external
68:         onlyAdmin
69:     {
70:         _updateMaxEarning(account_, maxEarning_);
71:     }

['66']

66:     function updateSponsor(
67:         address account_,
68:         address sponsor_
69:     )
70:         external
71:         onlyAdmin
72:     {
73:         _updateSponsor(account_, sponsor_);
74:     }

['122']

122:     function updateFsOf(
123:         address account_,
124:         uint fs_
125:     )
126:         external
127:         onlyAdmin
128:     {
129:         SProfile storage profileData = _profileOf[account_];
130:         profileData.ifs = LProfile.invertOf(fs_);
131:         emit UpdateFsOf(account_, fs_);
132:     }

['134']

134:     function updateBoosterOf(
135:         address account_,
136:         uint booster_
137:     )
138:         external
139:         onlyAdmin
140:     {
141:         SProfile storage profileData = _profileOf[account_];
142:         profileData.bonusBooster = booster_ - LPercentage.DEMI;
143:         emit UpdateBoosterOf(account_, booster_);
144:     }

[NonCritical-26] Specific imports should be used where possible so only used code is imported

Resolution

In many cases only some functionality is used from an import. In such cases it makes more sense to use {} to specify what to import and thus save gas whilst improving readability

Num of instances: 41

Findings

Click to show findings

['5']

5: import "@openzeppelin/contracts/access/Ownable.sol";

['6']

6: import "./Cashier.sol";

['7']

7: import "./Initializable.sol";

['7']

7: import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

['9']

9: import "./lib/LPercentage.sol";

['6']

6: import "./lib/LLocker.sol";

['9']

9: import "./lib/LProfile.sol";

['8']

8: import "./lib/LHelper.sol";

['10']

10: import "./lib/LLido.sol";

['5']

5: import "./modules/UseAccessControl.sol";

['10']

10: import "./modules/interfaces/IDToken.sol";

['11']

11: import "./modules/interfaces/IDistributor.sol";

['14']

14: import "./modules/interfaces/ILocker.sol";

['14']

14: import "./modules/interfaces/IEarning.sol";

['13']

13: import "./interfaces/IPoolFactory.sol";

['6']

6: import "./interfaces/IProfile.sol";

['19']

19: import "./interfaces/IDCT.sol";

['15']

15: import "./interfaces/IVoting.sol";

['21']

21: import "./interfaces/IEthSharing.sol";

['5']

5: import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

['7']

7: import "./modules/Vester.sol";

['6']

6: import "./PERC20.sol";

['8']

8: import "./interfaces/IDistributor.sol";

['10']

10: import "./interfaces/IDToken.sol";

['11']

11: import "./UseAccessControl.sol";

['9']

9: import "../interfaces/IProfile.sol";

['5']

5: import "./modules/AccessControl.sol";

['4']

4: import "../modules/interfaces/IPERC20.sol";

['4']

4: import "./IPERC20.sol";

['6']

6: import "./ICashier.sol";

['4']

4: import "../../lib/LLocker.sol";

['4']

4: import "../modules/interfaces/ICashier.sol";

['5']

5: import "./LPercentage.sol";

['6']

6: import "./LProfile.sol";

['8']

8: import "../lib/LLocker.sol";

['5']

5: import "../modules/interfaces/IWETH.sol";

['7']

7: import "./modules/DToken.sol";

['8']

8: import "./modules/Distributor.sol";

['8']

8: import "./modules/interfaces/IVester.sol";

['8']

8: import "./interfaces/IAccessControl.sol";

['12']

12: import "./modules/Cashier.sol";

[NonCritical-27] Old Solidity version

Resolution

Using outdated Solidity versions can lead to security risks and inefficiencies. It's recommended to adopt newer versions, ideally the latest, which as of now is 0.8.24. This ensures access to the latest bug fixes, features, and optimizations, particularly crucial for Layer 2 deployments. Regular updates to versions like 0.8.19 or later, up to 0.8.24, enhance contract security and performance.

Num of instances: 1

Findings

Click to show findings

[]

3: pragma solidity =0.8.8;

[NonCritical-28] Not all event definitions are utilizing indexed variables.

Resolution

Try to index as much as three variables in event declarations as this is more gas efficient when done on value type variables (uint, address etc) however not for bytes and string variables

Num of instances: 22

Findings

Click to show findings

['15']

15: event AddAdmin( // <= FOUND
16:         address account
17:     );

['19']

19: event RemoveAdmin( // <= FOUND
20:         address account
21:     );

['12']

12: event Distribute( // <= FOUND
13:         uint amount,
14:         uint regSupply
15:     );

['17']

17: event Claim( // <= FOUND
18:         address indexed holder,
19:         uint reward
20:     );

['22']

22: event UpdateRegBal( // <= FOUND
23:         address indexed holder,
24:         uint regBal
25:     );

['13']

13: event Clean( // <= FOUND
14:         uint amount
15:     );

['20']

20: event SetReward( // <= FOUND
21:         address indexed account,
22:         uint prevReward,
23:         uint reward
24:     );

['14']

14: event UpdateMaxEarning( // <= FOUND
15:         address indexed account,
16:         uint maxEarning
17:     );

['19']

19: event ShareCommission( // <= FOUND
20:         address indexed account,
21:         address indexed profile,
22:         uint sAmount
23:     );

['25']

25: event Withdraw( // <= FOUND
26:         address indexed account,
27:         uint amount,
28:         address dest
29:     );

['22']

22: event Withdraw( // <= FOUND
23:         address indexed account,
24:         address indexed poolOwner,
25:         address dest,
26:         uint amount
27:     );

['10']

10: event ConfigSystem( // <= FOUND
11:         uint devTeamPercent,
12:         uint defaultOwnerPercent,
13:         uint defaultUserPercent,
14:         uint defaultCode,
15:         bool inDefaultOnlyMode
16:     );

['33']

33: event UpdatePenaltyAddress( // <= FOUND
34:         address penaltyAddress
35:     );

['11']

11: event UpdateAthBalance( // <= FOUND
12:         address indexed account_,
13:         uint athBalance
14:     );

['48']

48: event SetInPrivateMode( // <= FOUND
49:         bool inPrivateMode
50:     );

['15']

15: event UpdateSponsor( // <= FOUND
16:         address indexed account,
17:         address indexed sponsor,
18:         uint sPercent
19:     );

['21']

21: event SetSPercent( // <= FOUND
22:         address indexed account,
23:         uint sPercent
24:     );

['26']

26: event UpdateFsOf( // <= FOUND
27:         address indexed account,
28:         uint fs
29:     );

['31']

31: event UpdateBoosterOf( // <= FOUND
32:         address indexed account,
33:         uint booster
34:     );

['88']

88: event CloseVote( // <= FOUND
89:         uint indexed voteId,
90:         uint cleanedVal,
91:         address dest
92:     );

['94']

94: event Finalize( // <= FOUND
95:         uint indexed voteId,
96:         bool isAttackerWon,
97:         uint winnerPower,
98:         uint winVal
99:     );

['101']

101: event ClaimFor( // <= FOUND
102:         uint indexed voteId,
103:         address indexed voter,
104:         uint winVal
105:     );

[NonCritical-29] uint/int variables should have the bit size defined explicitly

Resolution

Instead of using uint to declare uint258, explicitly define uint258 to ensure there is no confusion

Num of instances: 320

Findings

Click to show findings

['26']

26:         uint i = 0; // <= FOUND

['13']

12:     event Distribute(
13:         uint amount, // <= FOUND
14:         uint regSupply // <= FOUND
15:     );

['19']

17:     event Claim(
18:         address indexed holder,
19:         uint reward // <= FOUND
20:     );

['24']

22:     event UpdateRegBal(
23:         address indexed holder,
24:         uint regBal // <= FOUND
25:     );

['28']

28:         uint regBal; // <= FOUND

['29']

29:         uint lastRpt; // <= FOUND

['30']

30:         uint lastClaimedAt; // <= FOUND

['35']

35:     uint public rpt; // <= FOUND

['36']

36:     uint public regSupply; // <= FOUND

['38']

38:     uint constant private MFACTOR = 1e18; // <= FOUND

['55']

55:         uint addedAmount = _cashIn(); // <= FOUND

['76']

76:         uint bal = _dToken.balanceOf(holder_); // <= FOUND

['78']

78:         uint deltaRpt = rpt - holder.lastRpt; // <= FOUND

['79']

79:         uint reward; // <= FOUND

['82']

82:             uint unregBal = holder.regBal - bal; // <= FOUND

['83']

83:             uint removedReward = deltaRpt * unregBal / MFACTOR; // <= FOUND

['14']

13:     event Clean(
14:         uint amount // <= FOUND
15:     );

['17']

17:     uint private _lastestBalance; // <= FOUND

['55']

55:         uint incBalance = currentBalance() - _lastestBalance; // <= FOUND

['62']

60:     function _cashOut(
61:         address to_,
62:         uint amount_ // <= FOUND
63:     )
64:         internal
65:     {

['100']

100:         uint currentBal = currentBalance(); // <= FOUND

['102']

102:             uint amount = currentBal - _lastestBalance; // <= FOUND

['40']

36:     event Stake(
37:         bool indexed isEth,
38:         address indexed poolOwner,
39:         address indexed staker,
40:         uint amount, // <= FOUND
41:         uint duration, // <= FOUND
42:         uint powerMinted, // <= FOUND
43:         bool isFirstStake 
44:     );

['76']

76:     uint public _maxBooster = 2; // <= FOUND

['78']

78:     uint public _minDuration = 30 days; // <= FOUND

['79']

79:     uint public _maxDuration = 720 days; // <= FOUND

['80']

80:     uint public _minStakeETHAmount = 0.001e18; // <= FOUND

['81']

81:     uint public _minStakeDCTAmount = 7 ether; // <= FOUND

['83']

83:     uint public _maxSponsorAdv = 7; // <= FOUND

['84']

84:     uint public _maxSponsorAfter = 7 days; // <= FOUND

['86']

86:     uint public _lastMintAt; // <= FOUND

['87']

87:     uint public _mintingInt = 7; // <= FOUND

['89']

89:     uint public _attackFee = 1 ether; // <= FOUND

['90']

90:     uint public _minFreezeDuration = 1 days; // <= FOUND

['91']

91:     uint public _maxFreezeDuration = 7 days; // <= FOUND

['92']

92:     uint public _freezeDurationUnit = 7 days; // <= FOUND

['93']

93:     uint public _minDefenderFund = 0.001e18; // <= FOUND

['95']

95:     uint public _maxVoterPercent = 5000;  // <= FOUND

['96']

96:     uint public _minAttackerFundRate = 2500;  // <= FOUND

['98']

98:     uint public _bountyPullEarningPercent = 100;  // <= FOUND

['100']

100:     uint public _selfStakeAdvantage = 15000;  // <= FOUND

['102']

102:     uint public _isPausedAttack = 0; // <= FOUND

['104']

104:     uint public _dctTaxPercent = 100; // <= FOUND

['156']

156:             uint oldEP2PBalance = _eP2PDToken.balanceOf(pool.dctDistributor); // <= FOUND

['158']

158:             uint newEP2PBalance = LHelper.calEP2PDBalance( // <= FOUND
159:                 _profileC.fsOf(poolOwner_),
160:                 _profileC.boosterOf(poolOwner_),
161:                 p2UDtoken.totalSupply()
162:             );

['177']

177:         uint maxEarning = _eEarning.maxEarningOf(account_); // <= FOUND

['191']

191:         uint maxBoostVotePower = _dP2PDToken.athBalance(); // <= FOUND

['192']

192:         uint boostVotePower = _dP2PDToken.balanceOf(account_); // <= FOUND

['193']

193:         uint newBooster = LProfile.calBooster(boostVotePower, maxBoostVotePower, _maxBooster); // <= FOUND

['202']

198:     
199:     function _updateSponsor(
200:         address payable poolOwner_,
201:         address staker_,
202:         uint minSPercent_ // <= FOUND
203:     )
204:         internal
205:     {

['215']

215:         uint timeDiff = block.timestamp - profile.updatedAt; // <= FOUND

['220']

220:         uint sponsorDTokenBalance = p2UDtoken.balanceOf(profile.sponsor); // <= FOUND

['221']

221:         uint stakerDTokenBalance = p2UDtoken.balanceOf(staker_); // <= FOUND

['222']

222:         uint sponsorBonus = sponsorDTokenBalance * (_maxSponsorAdv - 1) // <= FOUND
223:             * timeDiff / _maxSponsorAfter;

['224']

224:         uint sponsorPower = sponsorDTokenBalance + sponsorBonus; // <= FOUND

['236']

234:     
235:     function _shareDevTeam(
236:         uint amount_ // <= FOUND
237:     )
238:         internal
239:     {

['245']

243:     
244:     function _sharePoolOwner(
245:         uint amount_, // <= FOUND
246:         address payable poolOwner_
247:     )
248:         internal
249:     {

['259']

257:     
258:     function _sharePoolUser(
259:         uint amount_, // <= FOUND
260:         address payable poolOwner_
261:     )
262:         internal
263:     {

['270']

267:     function _lock(
268:         bool isEth_,
269:         address payable poolOwner_,
270:         uint duration_ // <= FOUND
271:     )
272:         internal
273:         returns (uint)
274:     {

['277']

277:             uint value = _geth.balanceOf(address(this)); // <= FOUND

['284']

284:             uint value = _dct.balanceOf(address(this)); // <= FOUND

['297']

293:     function _stake(
294:         bool isEth_,
295:         address account_,
296:         address payable poolOwner_,
297:         uint duration_, // <= FOUND
298:         uint minSPercent_, // <= FOUND
299:         uint poolConfigCode_ // <= FOUND
300:     )
301:         internal
302:     {

['327']

323:         
324:         
325:         
326:         
327:         uint value = isEth_ ? _geth.balanceOf(address(this)) : _dct.balanceOf(address(this)); // <= FOUND

['325']

325:         uint minStakeAmount = isEth_ ? _minStakeETHAmount : _minStakeDCTAmount; // <= FOUND

['34']

34:         uint rd = LLocker.restDuration(oldLockData); // <= FOUND

['339']

339:         uint powerMinted; // <= FOUND

['343']

343:             (uint devTeamA, uint poolOwnerA, uint poolUserA, ) = // <= FOUND
344:                 _ethSharing.getSharingParts(poolOwner_, value, poolConfigCode_);

['350']

350:             uint aLock = _lock(isEth_, poolOwner_, duration_); // <= FOUND

['399']

398:     function _prepareWsteth(
399:         uint minWstethA_, // <= FOUND
400:         uint wstethA_ // <= FOUND
401:     )
402:         internal
403:     {

['414']

412:     function ethStake(
413:         address payable poolOwner_,
414:         uint duration_, // <= FOUND
415:         uint minSPercent_, // <= FOUND
416:         uint poolConfigCode_, // <= FOUND
417:         uint minWstethA_, // <= FOUND
418:         uint wstethA_ // <= FOUND
419:     )
420:         public
421:         payable
422:         tryPublicMint
423:     {

['430']

429:     function dctStake(
430:         uint amount_, // <= FOUND
431:         address payable poolOwner_,
432:         uint duration_ // <= FOUND
433:     )
434:         public
435:         payable
436:         tryPublicMint
437:     {

['439']

439:         uint taxA = LPercentage.getPercentA(amount_, _dctTaxPercent); // <= FOUND

['444']

443:         
444:         uint poolConfigCode = 0; // <= FOUND

['566']

543:     
563:     function lockWithdraw(
564:         bool isEth_,
565:         address payable poolOwner_,
566:         uint amount_, // <= FOUND
567:         address payable dest_,
568:         bool isForced_,
569:         uint minEthA_ // <= FOUND
570:     )
571:         public
572:         tryPublicMint
573:     {

['559']

559:         uint restAmount = oldLockData.amount - amount_; // <= FOUND

['567']

567:             uint burnedPower = LHelper.calBurnStakingPower(p2UDtoken.balanceOf(account), amount_, oldLockData.amount); // <= FOUND

['577']

577:             uint burnedPower = LHelper.calBurnStakingPower(_dP2PDToken.balanceOf(poolOwner_), amount_, oldLockData.amount); // <= FOUND

['594']

589:     
591:     function earningReinvest(
592:         bool isEth_,
593:         address payable poolOwner_,
594:         uint duration_, // <= FOUND
595:         uint amount_, // <= FOUND
596:         
597:         uint minSPercent_, // <= FOUND
598:         uint poolConfigCode_ // <= FOUND
599:     )
600:         external
601:     {

['603']

603:             uint realDuration = duration_ + LLocker.restDuration(oldLockData); // <= FOUND

['607']

607:             uint maxEarning = _eEarning.maxEarningOf(account); // <= FOUND

['617']

615:     function earningWithdraw(
616:         bool isEth_,
617:         uint amount_, // <= FOUND
618:         address payable dest_,
619:         uint minEthA_ // <= FOUND
620:         
621:     )
622:         public
623:     {

['643']

641:     function createVote(
642:         address defender_,
643:         uint dEthValue_, // <= FOUND
644:         uint voterPercent_, // <= FOUND
645:         uint freezeDuration_, // <= FOUND
646:         uint minWstethA_, // <= FOUND
647:         uint wstethA_ // <= FOUND
648:     )
649:         external
650:         payable
651:     {

['657']

657:         uint aEthValue = _geth.balanceOf(address(this)); // <= FOUND

['665']

665:         uint aQuorum = LHelper.calAQuorum( // <= FOUND
666:             aEthValue,
667:             dEthValue_,
668:             voterPercent_,
669:             freezeDuration_,
670:             _freezeDurationUnit
671:         );

['694']

693:     function votingClaimFor(
694:         uint voteId_, // <= FOUND
695:         address voter_
696:     )
697:         external
698:     {

['10']

10:     uint private _tps = 7 ether; // <= FOUND

['12']

12:     uint private _lastMintAt; // <= FOUND

['13']

13:     uint private _lastHalved; // <= FOUND

['14']

14:     uint constant public HALVING_INTERVAL = 7 days; // <= FOUND

['18']

18:     uint private _athBalance; // <= FOUND

['22']

22:     uint constant public MAX_SUPPLY = 3111666666 ether; // <= FOUND

['85']

85:         uint pastTime = block.timestamp - _lastMintAt; // <= FOUND

['92']

92:         uint mintingA = pendingA(); // <= FOUND

['12']

12:     uint private _totalDistributors; // <= FOUND

['27']

27:         uint n = _totalDistributors; // <= FOUND

['22']

20:     event SetReward(
21:         address indexed account,
22:         uint prevReward, // <= FOUND
23:         uint reward // <= FOUND
24:     );

['30']

29:     
30:     uint private _ptr; // <= FOUND

['30']

30:     uint constant private MFACTOR = 10 ** 18; // <= FOUND

['59']

59:         uint totalSupply = _dToken.totalSupply(); // <= FOUND

['61']

61:             uint amount = _cashIn(); // <= FOUND

['84']

84:             uint nextBalance = _dToken.balanceOf(account) - amount_; // <= FOUND

['91']

91:             uint nextBalance = _dToken.balanceOf(account) + amount_; // <= FOUND

['98']

96:     function _setAdjReward(
97:         address account_,
98:         uint prevReward, // <= FOUND
99:         uint reward_, // <= FOUND
100:         uint dTokenBalance_ // <= FOUND
101:     )
102:         internal
103:     {

['115']

114:     function calReward(
115:         uint ptr_, // <= FOUND
116:         uint dTokenBalance_, // <= FOUND
117:         int256 adjustedReward_
118:     )
119:         public
120:         pure
121:         returns(uint)
122:     {

['145']

145:         uint amount = rewardOf(account_); // <= FOUND

['16']

14:     event UpdateMaxEarning(
15:         address indexed account,
16:         uint maxEarning // <= FOUND
17:     );

['22']

19:     event ShareCommission(
20:         address indexed account,
21:         address indexed profile,
22:         uint sAmount // <= FOUND
23:     );

['27']

25:     event Withdraw(
26:         address indexed account,
27:         uint amount, // <= FOUND
28:         address dest
29:     );

['55']

53:     function _updateMaxEarning(
54:         address account_,
55:         uint maxEarning_ // <= FOUND
56:     )
57:         internal
58:     {

['65']

63:     function updateMaxEarning(
64:         address account_,
65:         uint maxEarning_ // <= FOUND
66:     )
67:         external
68:         onlyAdmin
69:     {

['78']

78:         uint amount = balanceOf(account_) - _sharedA[account_]; // <= FOUND

['84']

84:         uint sAmount; // <= FOUND

['119']

117:     function withdraw(
118:         address account_,
119:         uint amount_, // <= FOUND
120:         address dest_
121:     )
122:         external
123:         onlyAdmin
124:     {

['11']

10:     event ConfigSystem(
11:         uint devTeamPercent, // <= FOUND
12:         uint defaultOwnerPercent, // <= FOUND
13:         uint defaultUserPercent, // <= FOUND
14:         uint defaultCode, // <= FOUND
15:         bool inDefaultOnlyMode
16:     );

['23']

23:     uint public devTeamPercent = 1 * 100; // <= FOUND

['24']

24:     uint public defaultOwnerPercent = 2 * 100; // <= FOUND

['25']

25:     uint public defaultUserPercent = 3 * 100; // <= FOUND

['26']

26:     uint public defaultCode = getCode(defaultOwnerPercent, defaultUserPercent); // <= FOUND

['31']

31:         uint ownerPercent; // <= FOUND

['32']

32:         uint userPercent; // <= FOUND

['33']

33:         uint code; // <= FOUND

['41']

39:     function initEthSharing(
40:         address accessControl_,
41:         uint devTeamPercent_, // <= FOUND
42:         uint defaultOwnerPercent_, // <= FOUND
43:         uint defaultUserPercent_, // <= FOUND
44:         bool inDefaultOnlyMode_
45: 
46:     )
47:         external
48:         initializer
49:     {

['60']

59:     function getCode(
60:         uint ownerPercent_, // <= FOUND
61:         uint userPercent_ // <= FOUND
62:     )
63:         public
64:         pure
65:         returns(uint)
66:     {

['83']

81:     function _configPool(
82:         address poolOwner_,
83:         uint ownerPercent_, // <= FOUND
84:         uint userPercent_ // <= FOUND
85:     )
86:         internal
87:     {

['98']

97:     function _configSystem(
98:         uint devTeamPercent_, // <= FOUND
99:         uint defaultOwnerPercent_, // <= FOUND
100:         uint defaultUserPercent_, // <= FOUND
101:         bool inDefaultOnlyMode_
102:     )
103:         internal
104:     {

['123']

122:     function configSystem(
123:         uint devTeamPercent_, // <= FOUND
124:         uint defaultOwnerPercent_, // <= FOUND
125:         uint defaultUserPercent_, // <= FOUND
126:         bool inDefaultOnlyMode_
127:     )
128:         external
129:         onlyAdmin
130:     {

['166']

165:     function configPool(
166:         uint ownerPercent_, // <= FOUND
167:         uint userPercent_ // <= FOUND
168:     )
169:         external
170:     {

['187']

186:     function getDevTeamPart(
187:         uint value_ // <= FOUND
188:     )
189:         public
190:         view
191:         returns(uint)
192:     {

['193']

193:         uint amount = LPercentage.getPercentA(value_, devTeamPercent); // <= FOUND

['198']

197:     function getSysExcPart(
198:         uint value_ // <= FOUND
199:     )
200:         public
201:         view
202:         returns(uint)
203:     {

['204']

204:         uint amount = value_ - getDevTeamPart(value_); // <= FOUND

['210']

208:     function getPoolOwnerPart(
209:         address poolOwner_,
210:         uint value_ // <= FOUND
211:     )
212:         public
213:         view
214:         returns(uint)
215:     {

['216']

216:         uint sysExcPart = getSysExcPart(value_); // <= FOUND

['218']

218:         uint amount = LPercentage.getPercentA(sysExcPart, poolConfig.ownerPercent); // <= FOUND

['224']

222:     function getPoolUserPart(
223:         address poolOwner_,
224:         uint value_ // <= FOUND
225:     )
226:         public
227:         view
228:         returns(uint)
229:     {

['232']

232:         uint amount = LPercentage.getPercentA(sysExcPart, poolConfig.userPercent); // <= FOUND

['238']

236:     function getLockedPart(
237:         address poolOwner_,
238:         uint value_ // <= FOUND
239:     )
240:         public
241:         view
242:         returns(uint)
243:     {

['245']

245:         uint amount = sysExcPart // <= FOUND
246:             - getPoolOwnerPart(poolOwner_, value_)
247:             - getPoolUserPart(poolOwner_, value_);

['253']

251:     function getSharingParts(
252:         address poolOwner_,
253:         uint value_, // <= FOUND
254:         uint code_ // <= FOUND
255:     )
256:         public
257:         view
258:         returns(uint devTeamA, uint poolOwnerA, uint poolUserA, uint lockedA) // <= FOUND
259:     {

['19']

17:     function updateMaxEarning(
18:         address account_,
19:         uint maxEarning_ // <= FOUND
20:     )
21:         external;

['36']

34:     function withdraw(
35:         address account_,
36:         uint amount_, // <= FOUND
37:         address dest_
38:     )
39:         external;

['7']

5:     function initEthSharing(
6:         address accessControl_,
7:         uint devTeamPercent_, // <= FOUND
8:         uint defaultOwnerPercent_, // <= FOUND
9:         uint defaultUserPercent_, // <= FOUND
10:         bool inDefaultOnlyMode_
11: 
12:     )
13:         external;

['16']

15:     function configSystem(
16:         uint devTeamPercent_, // <= FOUND
17:         uint defaultOwnerPercent_, // <= FOUND
18:         uint defaultUserPercent_, // <= FOUND
19:         bool inDefaultOnlyMode_
20:     )
21:         external;

['35']

34:     function configPool(
35:         uint ownerPercent_, // <= FOUND
36:         uint userPercent_  // <= FOUND
37:     )
38:         external;

['48']

47:     function getDevTeamPart(
48:         uint value_ // <= FOUND
49:     )
50:         external
51:         view
52:         returns(uint);

['55']

54:     function getSysExcPart(
55:         uint value_ // <= FOUND
56:     )
57:         external
58:         view
59:         returns(uint);

['63']

61:     function getPoolOwnerPart(
62:         address poolOwner_,
63:         uint value_ // <= FOUND
64:     )
65:         external
66:         view
67:         returns(uint);

['71']

69:     function getPoolUserPart(
70:         address poolOwner_,
71:         uint value_ // <= FOUND
72:     )
73:         external
74:         view
75:         returns(uint);

['79']

77:     function getLockedPart(
78:         address poolOwner_,
79:         uint value_ // <= FOUND
80:     )
81:         external
82:         view
83:         returns(uint);

['87']

85:     function getSharingParts(
86:         address poolOwner_,
87:         uint value_, // <= FOUND
88:         uint code_ // <= FOUND
89:     )
90:         external
91:         view
92:         returns(uint devTeamA, uint poolOwnerA, uint poolUserA, uint lockedA); // <= FOUND

['20']

17:     function lock(
18:         address account_,
19:         address poolOwner_,
20:         uint duration_ // <= FOUND
21:     )
22:         external;

['28']

24:     function withdraw(
25:         address account_,
26:         address poolOwner_,
27:         address dest_,
28:         uint amount_, // <= FOUND
29:         bool isForced_
30:     )
31:         external;

['29']

27:     function mint(
28:         address account_,
29:         uint amount_ // <= FOUND
30:     )
31:         external;

['35']

33:     function burn(
34:         address account_,
35:         uint amount_ // <= FOUND
36:     )
37:         external;

['7']

7:         uint sPercent; // <= FOUND

['8']

8:         uint nextSPercent; // <= FOUND

['9']

9:         uint updatedAt; // <= FOUND

['10']

10:         uint ifs; // <= FOUND

['11']

11:         uint bonusBooster; // <= FOUND

['29']

27:     function getSponsorPart(
28:         address account_,
29:         uint amount_ // <= FOUND
30:     )
31:         external
32:         view
33:         returns(address sponsor, uint sAmount); // <= FOUND

['36']

35:     function setSPercent(
36:         uint sPercent_ // <= FOUND
37:     )
38:         external;

['41']

40:     function setDefaultSPercentConfig(
41:         uint sPercent_ // <= FOUND
42:     )
43:         external;

['46']

45:     function setMinSPercentConfig(
46:         uint sPercent_ // <= FOUND
47:     )
48:         external;

['52']

50:     function updateFsOf(
51:         address account_,
52:         uint fs_ // <= FOUND
53:     )
54:         external;

['58']

56:     function updateBoosterOf(
57:         address account_,
58:         uint booster_ // <= FOUND
59:     )
60:         external;

['18']

16:     function lock(
17:         address account_,
18:         uint duration_, // <= FOUND
19:         uint cliff_ // <= FOUND
20:     )
21:         external;

['26']

23:     function transferLock(
24:         address from_,
25:         address to_,
26:         uint amount_ // <= FOUND
27:     )
28:         external;

['40']

35:     function currentLockData(
36:         address account_
37:     )
38:         external
39:         view
40:         returns(uint restA, uint restDuration); // <= FOUND

['34']

34:         uint aEthValue; // <= FOUND

['35']

35:         uint dEthValue; // <= FOUND

['37']

37:         uint voterPercent; // <= FOUND

['38']

38:         uint aQuorum; // <= FOUND

['40']

40:         uint startedAt; // <= FOUND

['41']

41:         uint endAt; // <= FOUND

['43']

43:         uint attackerPower; // <= FOUND

['44']

44:         uint defenderPower; // <= FOUND

['48']

48:         uint totalClaimed; // <= FOUND

['53']

53:         uint winVal; // <= FOUND

['54']

54:         uint winnerPower; // <= FOUND

['36']

32:     function createVote(
33:         address attacker_,
34:         address defender_,
35: 
36:         uint aEthValue_, // <= FOUND
37:         uint dEthValue_, // <= FOUND
38: 
39:         uint voterPercent_, // <= FOUND
40:         uint aQuorum_, // <= FOUND
41: 
42:         uint startedAt_, // <= FOUND
43:         uint endAt_ // <= FOUND
44:     )
45:         external;

['48']

47:     function getVote(
48:         uint voteId_ // <= FOUND
49:     )
50:         external
51:         view
52:         returns(SVoteBasicInfo memory);

['55']

54:     function claimFor(
55:         uint voteId_, // <= FOUND
56:         address voter_
57:     )
58:         external;

['12']

11:     function calEP2PDBalance(
12:         uint fs_, // <= FOUND
13:         uint booster_, // <= FOUND
14:         uint totalP2UBalance_ // <= FOUND
15:     )
16:         internal
17:         pure
18:         returns(uint)
19:     {

['25']

23:     function calMintStakingPower(
24:         LLocker.SLock memory oldLockData,
25:         uint lockAmount_, // <= FOUND
26:         uint lockTime_, // <= FOUND
27:         bool isSelfStake_,
28:         uint selfStakeAdvantage_ // <= FOUND
29:     )
30:         internal
31:         view
32:         returns(uint)
33:     {

['35']

35:         uint oldALock = oldLockData.amount; // <= FOUND

['36']

36:         uint dLockForOldA = lockTime_; // <= FOUND

['37']

37:         uint dLockForStakeA = lockTime_ + rd; // <= FOUND

['41']

41:         uint rs = (oldALock * calMultiplierForOldAmount(dLockForOldA) + lockAmount_ * calMultiplier(dLockForStakeA)) / LPercentage.DEMI; // <= FOUND

['50']

49:     function calBurnStakingPower(
50:         uint powerBalance_, // <= FOUND
51:         uint unlockedA_, // <= FOUND
52:         uint totalLockedA_ // <= FOUND
53:     )
54:         internal
55:         pure
56:         returns(uint)
57:     {

['62']

61:     function calFs(
62:         uint earningBalance_, // <= FOUND
63:         uint maxEarning_ // <= FOUND
64:     )
65:         internal
66:         pure
67:         returns(uint)
68:     {

['69']

69:         uint max = maxEarning_; // <= FOUND

['81']

79:     
80:     function calMultiplierForOldAmount(
81:         uint lockTime_ // <= FOUND
82:     )
83:         internal
84:         pure
85:         returns(uint)
86:     {

['86']

86:         uint x = lockTime_ * LPercentage.DEMI / 30 days; // <= FOUND

['87']

87:         uint rs = (1300 * x / LPercentage.DEMI); // <= FOUND

['92']

91:     function calMultiplier(
92:         uint lockTime_ // <= FOUND
93:     )
94:         internal
95:         pure
96:         returns(uint)
97:     {

['99']

99:         uint rs = (1300 * x / LPercentage.DEMI) + 8800; // <= FOUND

['104']

103:     function calAQuorum(
104:         uint aEthValue_, // <= FOUND
105:         uint dEthValue_, // <= FOUND
106:         uint voterPercent_, // <= FOUND
107:         uint freezeDuration_, // <= FOUND
108:         uint freezeDurationUnit_ // <= FOUND
109:     )
110:         internal
111:         pure
112:         returns(uint)
113:     {

['114']

114:         uint tmp = LPercentage.DEMI - voterPercent_; // <= FOUND

['115']

115:         uint leverage = LPercentage.DEMIE2 * // <= FOUND
116:             dEthValue_ * freezeDuration_ / aEthValue_ / freezeDurationUnit_ / tmp;

['14']

14:         uint amount0Desired; // <= FOUND

['15']

15:         uint amount1Desired; // <= FOUND

['16']

16:         uint amount0Min; // <= FOUND

['17']

17:         uint amount1Min; // <= FOUND

['19']

19:         uint deadline; // <= FOUND

['47']

42:     function mint(
43:         MintParams calldata params
44:     )
45:         external
46:         payable
47:         returns (uint tokenId, uint128 liquidity, uint amount0, uint amount1); // <= FOUND

['50']

50:         uint tokenId; // <= FOUND

['60']

58:     function increaseLiquidity(
59:         IncreaseLiquidityParams calldata params
60:     ) external payable returns (uint128 liquidity, uint amount0, uint amount1); // <= FOUND

['72']

70:     function decreaseLiquidity(
71:         DecreaseLiquidityParams calldata params
72:     ) external payable returns (uint amount0, uint amount1); // <= FOUND

['83']

81:     function collect(
82:         CollectParams calldata params
83:     ) external payable returns (uint amount0, uint amount1); // <= FOUND

['94']

93:         
94:         uint amountIn; // <= FOUND

['94']

94:         uint amountOutMinimum; // <= FOUND

['93']

93:         uint amountIn; // <= FOUND

['165']

164:     function add(
165:         uint tokenId_ // <= FOUND
166:     )
167:         internal
168:         returns(uint128 liquidity)
169:     {

['170']

170:         uint amount = weth.balanceOf(address(this)); // <= FOUND

['171']

171:         uint wethA = amount / 2; // <= FOUND

['173']

173:         uint wstethA = wsteth.balanceOf(address(this)); // <= FOUND

['178']

177:     function remove(
178:         uint tokenId_, // <= FOUND
179:         uint128 decLiqA_
180:     )
181:         internal
182:     {

['201']

200:     function buyWsteth(
201:         uint wethA_ // <= FOUND
202:     )
203:         public
204:     {

['217']

216:     function allToWsteth(
217:         uint minWstethBal_ // <= FOUND
218:     )
219:         public
220:     {

['227']

226:     function allToEth(
227:         uint minEthBal_ // <= FOUND
228:     )
229:         public
230:     {

['237']

236:     function sellWsteth(
237:         uint wstethA_ // <= FOUND
238:     )
239:         public
240:     {

['264']

264:         uint amount = address(this).balance; // <= FOUND

['271']

270:     function mintNewPosition(
271:         uint wethA_, // <= FOUND
272:         uint wstethA_ // <= FOUND
273:     )
274:         internal
275:         returns
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1) // <= FOUND
277:     {

['283']

283:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_; // <= FOUND

['284']

284:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_; // <= FOUND

['307']

306:     function increaseLiquidityCurrentRange(
307:         uint tokenId_, // <= FOUND
308:         uint wethA_, // <= FOUND
309:         uint wstethA_ // <= FOUND
310:     ) internal returns (uint128 liquidity, uint amount0, uint amount1) { // <= FOUND

['284']

283:         
284:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_; // <= FOUND

['336']

335:     function collectAllFees(
336:         uint tokenId_ // <= FOUND
337:     ) internal returns (uint amount0, uint amount1) { // <= FOUND

['350']

349:     function getLiquidity(
350:         uint tokenId_ // <= FOUND
351:     )
352:         internal
353:         view
354:         returns (uint128)
355:     {

['361']

360:     function decreaseLiquidityCurrentRange(
361:         uint tokenId_, // <= FOUND
362:         uint128 decLiqA_
363:     )
364:         internal
365:         returns (uint amount0, uint amount1) // <= FOUND
366:     {

['384']

381:     function swapExactInputSingleHop(
382:         address tokenIn,
383:         address tokenOut,
384:         uint amountIn // <= FOUND
385:     )
386:         internal
387:         returns (uint amountOut) {

['10']

10:         uint amount; // <= FOUND

['11']

11:         uint duration; // <= FOUND

['35']

35:         uint pastTime = block.timestamp - lockData_.startedAt; // <= FOUND

['45']

43:     function prolong(
44:         SLock storage lockData_,
45:         uint amount_, // <= FOUND
46:         uint duration_ // <= FOUND
47:     )
48:         internal
49:     {

['58']

58:         uint rd = restDuration(lockData_); // <= FOUND

['70']

68:     function isUnlocked(
69:         SLock memory lockData_,
70:         uint fs_, // <= FOUND
71:         bool isPoolOwner_
72:     )
73:         internal
74:         view
75:         returns(bool)
76:     {

['77']

77:         uint mFactor = isPoolOwner_ ? 2 * LPercentage.DEMI - fs_ : fs_; // <= FOUND

['78']

78:         uint duration = lockData_.duration * mFactor / LPercentage.DEMI; // <= FOUND

['79']

79:         uint elapsedTime = block.timestamp - lockData_.startedAt; // <= FOUND

['85']

83:     function calDuration(
84:         SLock memory lockData_,
85:         uint fs_, // <= FOUND
86:         bool isPoolOwner_
87:     )
88:         internal
89:         pure
90:         returns(uint)
91:     {

['6']

6:     uint constant public DEMI = 10000; // <= FOUND

['7']

7:     uint constant public DEMIE2 = DEMI * DEMI; // <= FOUND

['8']

8:     uint constant public DEMIE3 = DEMIE2 * DEMI; // <= FOUND

['11']

10:     function validatePercent(
11:         uint percent_ // <= FOUND
12:     )
13:         internal
14:         pure
15:     {

['21']

20:     function getPercentA(
21:         uint value, // <= FOUND
22:         uint percent // <= FOUND
23:     )
24:         internal
25:         pure
26:         returns(uint)
27:     {

['17']

12:     
16:     function invertOf(
17:         uint value_ // <= FOUND
18:     )
19:         internal
20:         pure
21:         returns(uint)
22:     {

['23']

22:     function calBooster(
23:         uint boostVotePower_, // <= FOUND
24:         uint maxBoostVotePower_, // <= FOUND
25:         uint maxBooster_ // <= FOUND
26:     )
27:         pure
28:         internal
29:         returns(uint)
30:     {

['31']

31:         uint max = boostVotePower_ > maxBoostVotePower_ ? boostVotePower_ : maxBoostVotePower_; // <= FOUND

['26']

22:     event Withdraw(
23:         address indexed account,
24:         address indexed poolOwner,
25:         address dest,
26:         uint amount // <= FOUND
27:     );

['62']

59:     function lock(
60:         address account_,
61:         address poolOwner_,
62:         uint duration_ // <= FOUND
63:     )
64:         external
65:         onlyAdmin
66:     {

['43']

43:         uint amount = _cashIn(); // <= FOUND

['79']

74:     
75:     function withdraw(
76:         address account_,
77:         address poolOwner_,
78:         address dest_,
79:         uint amount_, // <= FOUND
80:         bool isForced_
81:     )
82:         external
83:         onlyApprovedAdmin(account_)
84:     {

['91']

87:     function _withdraw(
88:         address account_,
89:         address poolOwner_,
90:         address dest_,
91:         uint amount_, // <= FOUND
92:         bool isForced_
93:     )
94:         internal
95:     {

['99']

99:         uint fs = _profileC.fsOf(poolOwner_); // <= FOUND

['103']

103:         uint duration = LLocker.calDuration(lockData, fs, isPoolOwner); // <= FOUND

['104']

104:         uint pastTime = block.timestamp - lockData.startedAt; // <= FOUND

['110']

110:         uint total = amount_; // <= FOUND

['111']

111:         uint receivedA = total * pastTime / duration; // <= FOUND

['13']

11:     event UpdateAthBalance(
12:         address indexed account_,
13:         uint athBalance // <= FOUND
14:     );

['28']

26:     function _updateAthBalance(
27:         address account_,
28:         uint balance_ // <= FOUND
29:     )
30:         internal
31:     {

['157']

155:     function mint(
156:         address account_,
157:         uint amount_ // <= FOUND
158:     )
159:         external
160:         onlyAdmin
161:     {

['167']

165:     function burn(
166:         address account_,
167:         uint amount_ // <= FOUND
168:     )
169:         external
170:         virtual
171:         onlyAdmin
172:     {

['52']

50:     function _deploy(
51:         bytes memory bytecode,
52:         uint _salt // <= FOUND
53:     )
54:         internal
55:         returns(address addr)
56:     {

['73']

73:         uint salt = uint256(uint160(owner_)); // <= FOUND

['27']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_, // <= FOUND
28:         uint vestingDur_, // <= FOUND
29:         uint vestingPercent_, // <= FOUND
30:         uint cliff_ // <= FOUND
31:     )
32:         internal
33:     {

['34']

34:         uint vestingA = amount_ * vestingPercent_ / 10000; // <= FOUND

['35']

35:         uint directA = amount_ - vestingA; // <= FOUND

['44']

41:     function transferLock(
42:         address from_,
43:         address to_,
44:         uint amount_ // <= FOUND
45:     )
46:         external
47:         onlyOwner
48:     {

['75']

63:     
73:     function withdraw(
74:         address dest_,
75:         uint amount_ // <= FOUND
76:     )
77:         external
78:         onlyOwner
79:     {

['18']

15:     event UpdateSponsor(
16:         address indexed account,
17:         address indexed sponsor,
18:         uint sPercent // <= FOUND
19:     );

['23']

21:     event SetSPercent(
22:         address indexed account,
23:         uint sPercent // <= FOUND
24:     );

['28']

26:     event UpdateFsOf(
27:         address indexed account,
28:         uint fs // <= FOUND
29:     );

['33']

31:     event UpdateBoosterOf(
32:         address indexed account,
33:         uint booster // <= FOUND
34:     );

['37']

37:     uint public _defaultSPercent = 1000;  // <= FOUND

['38']

38:     uint public _minSPercent = 1000;  // <= FOUND

['77']

76:     function setSPercent(
77:         uint sPercent_ // <= FOUND
78:     )
79:         external
80:     {

['103']

102:     function setDefaultSPercentConfig(
103:         uint sPercent_ // <= FOUND
104:     )
105:         external
106:         onlyAdmin
107:     {

['113']

112:     function setMinSPercentConfig(
113:         uint sPercent_ // <= FOUND
114:     )
115:         external
116:         onlyAdmin
117:     {

['124']

122:     function updateFsOf(
123:         address account_,
124:         uint fs_ // <= FOUND
125:     )
126:         external
127:         onlyAdmin
128:     {

['136']

134:     function updateBoosterOf(
135:         address account_,
136:         uint booster_ // <= FOUND
137:     )
138:         external
139:         onlyAdmin
140:     {

['158']

156:     function getSponsorPart(
157:         address account_,
158:         uint amount_ // <= FOUND
159:     )
160:         external
161:         view
162:         returns(address sponsor, uint sAmount) // <= FOUND
163:     {

['37']

35:     function lock(
36:         address account_,
37:         uint duration_, // <= FOUND
38:         uint cliff_ // <= FOUND
39:     )
40:         external
41:         onlyAdmin
42:     {

['55']

52:     function transferLock(
53:         address from_,
54:         address to_,
55:         uint amount_ // <= FOUND
56:     )
57:         external
58:         onlyAdmin
59:     {

['65']

65:         uint duration = lockData.duration; // <= FOUND

['82']

82:         uint airdrop = _cashIn(); // <= FOUND

['83']

83:         (uint restA, uint restDuration) = currentLockData(account_); // <= FOUND

['84']

84:         uint toUnlockA = lockData.amount - restA; // <= FOUND

['88']

88:         uint toTransferA = toUnlockA + airdrop; // <= FOUND

['128']

109:     
123:     function currentLockData(
124:         address account_
125:     )
126:         public
127:         view
128:         returns(uint restA, uint restDuration) // <= FOUND
129:     {

['20']

19:     modifier onlyInVotingTime(
20:         uint voteId_ // <= FOUND
21:     )
22:     {

['59']

58:     event CreateVote(
59:         uint indexed voteId, // <= FOUND
60: 
61:         address indexed attacker,
62:         address indexed defender,
63: 
64:         uint aEthValue, // <= FOUND
65:         uint dEthValue, // <= FOUND
66: 
67:         uint voterPercent, // <= FOUND
68:         uint aQuorum, // <= FOUND
69: 
70:         uint startedAt, // <= FOUND
71:         uint endAt // <= FOUND
72:     );

['75']

74:     event RemoveVoter(
75:         uint indexed voteId, // <= FOUND
76:         address indexed voter,
77:         bool indexed isForAttacker,
78:         uint power // <= FOUND
79:     );

['82']

81:     event AddVoter(
82:         uint indexed voteId, // <= FOUND
83:         address indexed voter,
84:         bool indexed isForAttacker,
85:         uint power // <= FOUND
86:     );

['89']

88:     event CloseVote(
89:         uint indexed voteId, // <= FOUND
90:         uint cleanedVal, // <= FOUND
91:         address dest
92:     );

['95']

94:     event Finalize(
95:         uint indexed voteId, // <= FOUND
96:         bool isAttackerWon,
97:         uint winnerPower, // <= FOUND
98:         uint winVal // <= FOUND
99:     );

['102']

101:     event ClaimFor(
102:         uint indexed voteId, // <= FOUND
103:         address indexed voter,
104:         uint winVal // <= FOUND
105:     );

['108']

108:     uint private _totalVotes; // <= FOUND

['140']

136:     function createVote(
137:         address attacker_,
138:         address defender_,
139: 
140:         uint aEthValue_, // <= FOUND
141:         uint dEthValue_, // <= FOUND
142: 
143:         uint voterPercent_, // <= FOUND
144:         uint aQuorum_, // <= FOUND
145: 
146:         uint startedAt_, // <= FOUND
147:         uint endAt_ // <= FOUND
148:     )
149:         external
150:         onlyAdmin
151:     {

['152']

152:         uint voteId = _totalVotes; // <= FOUND

['167']

167:         uint inVal = _cashIn(); // <= FOUND

['192']

191:     function _removeVoter(
192:         uint voteId_, // <= FOUND
193:         address voter_
194:     )
195:         internal
196:     {

['198']

198:         uint power = vote.powerOf[voter_]; // <= FOUND

['211']

210:     function _addVoter(
211:         uint voteId_, // <= FOUND
212:         address voter_,
213:         bool isForAttacker_
214:     )
215:         internal
216:     {

['220']

220:         uint power = _votingToken.balanceOf(voter_); // <= FOUND

['222']

222:         uint voteDuration = vote.endAt - vote.startedAt; // <= FOUND

['223']

223:         uint pastTime = block.timestamp - vote.startedAt; // <= FOUND

['224']

224:         uint restDuration = voteDuration - pastTime; // <= FOUND

['243']

242:     function updatePower(
243:         uint voteId_, // <= FOUND
244:         bool isForAttacker_
245:     )
246:         external
247:         onlyInVotingTime(voteId_)
248:     {

['254']

253:     function _tryFinalize(
254:         uint voteId_ // <= FOUND
255:     )
256:         internal
257:     {

['263']

263:         uint totalPower = vote.attackerPower + vote.defenderPower; // <= FOUND

['264']

264:         uint reqPower = LPercentage.getPercentA(totalPower, vote.aQuorum); // <= FOUND

['270']

270:             uint toWinnerVal = vote.aEthValue + vote.dEthValue - vote.winVal; // <= FOUND

['280']

280:             uint unfreezeVal = vote.dEthValue; // <= FOUND

['281']

281:             uint rewardWinnerVal = vote.aEthValue - vote.winVal; // <= FOUND

['298']

297:     function claimFor(
298:         uint voteId_, // <= FOUND
299:         address voter_
300:     )
301:         external
302:         onlyAdmin
303:     {

['316']

316:         uint winVal = vote.winVal * vote.powerOf[voter_] / vote.winnerPower; // <= FOUND

['328']

327:     function closeVote(
328:         uint voteId_, // <= FOUND
329:         address dest_
330:     )
331:         external
332:         onlyAdmin
333:     {

['339']

339:         uint cleanedVal = vote.winVal - vote.totalClaimed; // <= FOUND

['345']

344:     function getVote(
345:         uint voteId_ // <= FOUND
346:     )
347:         external
348:         view
349:         returns(IVoting.SVoteBasicInfo memory)
350:     {

[NonCritical-30] Functions with array parameters should have length checks in place

Resolution

Functions in Solidity that accept array parameters should incorporate length checks as a security measure. This is to prevent potential overflow errors, unwanted gas consumption, and manipulation attempts. Without length checks, an attacker could pass excessively large arrays as input, causing excessive computation and potentially causing the function to exceed the block gas limit, leading to a denial-of-service. Additionally, unexpected array sizes could lead to logic errors within the function. As a resolution, always validate array length at the start of functions handling array inputs, ensuring it aligns with the expectations of the function logic. This makes the code more robust and predictable.

Num of instances: 2

Findings

Click to show findings

['108']

108:     function initController(
109:         address[] memory contracts_ // <= FOUND
110:     )
111:         external
112:         initializer
113:     {
114:         _geth = IERC20(contracts_[0]);
115:         _dct = IDCT(contracts_[1]);
116: 
117:         initUseAccessControl(contracts_[2]);
118:         _devTeam = IDistributor(contracts_[3]);
119:         _poolFactory = IPoolFactory(contracts_[4]);
120:         _profileC = IProfile(contracts_[5]);
121: 
122:         _eLocker = ILocker(contracts_[6]);
123:         _eP2PDToken = IDToken(contracts_[7]);
124:         
125:         _eEarning = IEarning(contracts_[8]);
126: 
127:         _dLocker = ILocker(contracts_[9]);
128:         _dP2PDToken = IDToken(contracts_[10]);
129:         _dP2PDistributor = IDistributor(contracts_[11]);
130:         _dEarning = IEarning(contracts_[12]);
131: 
132:         _voting = IVoting(contracts_[13]);
133: 
134:         
135:         _eDP2PDistributor = IDistributor(contracts_[14]);
136:         
137:         _dDP2PDistributor = IDistributor(contracts_[15]);
138: 
139:         
140:         
141:         _ethSharing = IEthSharing(contracts_[16]);
142: 
143:         _dP2PDToken.activeAthRecord();
144:     }

['733']

733:     function updateConfigs(
734:         uint[] memory values_ // <= FOUND
735:     )
736:         external
737:         onlyAdmin
738:     {
739: 
740:         require(values_[0] <= 300, "max 3%");
741:         _bountyPullEarningPercent = values_[0];
742: 
743:         _maxBooster = values_[1];
744: 
745:         _maxSponsorAdv = values_[2];
746:         _maxSponsorAfter = values_[3];
747: 
748:         _attackFee = values_[4];
749:         _maxVoterPercent = values_[5];
750:         _minAttackerFundRate = values_[6];
751:         _freezeDurationUnit = values_[7];
752:         _selfStakeAdvantage = values_[8];
753: 
754:         _profileC.setDefaultSPercentConfig(values_[9]);
755:         _isPausedAttack = values_[10];
756: 
757:         _profileC.setMinSPercentConfig(values_[11]);
758: 
759:         _dctTaxPercent = values_[12];
760: 
761:         _minFreezeDuration = values_[13];
762:         _maxFreezeDuration = values_[14];
763: 
764:         _minStakeETHAmount = values_[15];
765:         _minStakeDCTAmount = values_[16];
766: 
767:         _minDefenderFund = values_[17];
768: 
769:         emit AdminUpdateConfig(values_);
770:     }

[NonCritical-31] Interface imports should be declared first

Resolution

Amend the ordering of imports to import interfaces first followed by other imports

Num of instances: 13

Findings

Click to show findings

['8']

3: 
5: pragma solidity =0.8.8;
6: pragma abicoder v2;
7: 
8: import "./Cashier.sol"; // <= FOUND
9: 
10: import "../lib/LLocker.sol"; // <= FOUND
11: import "../interfaces/IProfile.sol"; // <= FOUND
12: 
13: import "./UseAccessControl.sol"; // <= FOUND
14: 
15: contract Vester is Cashier, UseAccessControl {
16:     using LLocker for *;
17: 
18:     event UpdateLockData(
19:         address indexed account,
20:         LLocker.SLock lockData
21:     );
22: 
23:     mapping(address => LLocker.SLock) private _lockData;
24: 
25:     function initVester(
26:         address accessControl_,
27:         address token_,
28:         address cleanTo_
29:     )
30:         public
31:         initializer
32:     {
33:         _initCashier(token_, cleanTo_);
34:         initUseAccessControl(accessControl_);
35:     }
36: 
37:     function lock(
38:         address account_,
39:         uint duration_,
40:         uint cliff_
41:     )
42:         external
43:         onlyAdmin
44:     {
45:         uint amount = _cashIn();
46:         unlock(account_);
47:         LLocker.SLock storage lockData = _lockData[account_];
48:         lockData.amount += amount;
49:         lockData.duration = duration_;
50:         lockData.startedAt = block.timestamp + cliff_;
51:         emit UpdateLockData(account_, _lockData[account_]);
52:     }
53: 
54:     function transferLock(
55:         address from_,
56:         address to_,
57:         uint amount_
58:     )
59:         external
60:         onlyAdmin
61:     {
62:         unlock(from_);
63:         unlock(to_);

['8']

3: 
5: pragma solidity =0.8.8;
6: pragma abicoder v2;
7: 
8: import "./PERC20.sol"; // <= FOUND
9: 
10: import "./interfaces/IDistributor.sol"; // <= FOUND
11: 
12: contract DToken is PERC20 {
13:     mapping(uint => IDistributor) private _distributors;
14:     uint private _totalDistributors;
15: 
16:     function initDToken(
17:         address accessControl_,
18:         bool inPrivateMode_,
19:         string memory name_,
20:         string memory symbol_,
21:         address[] memory distributorAddrs_
22:     )
23:         public
24:         initializer
25:     {
26:         initPERC20(accessControl_, inPrivateMode_, name_, symbol_);
27:         _totalDistributors = distributorAddrs_.length;
28:         uint i = 0;
29:         uint n = _totalDistributors;
30:         while (i < n) {
31:             _distributors[i] = IDistributor(distributorAddrs_[i]);
32:             i++;
33:         }
34:     }
35: 
36:     function _beforeTokenTransfer(
37:         address from_,
38:         address to_,
39:         uint256 amount_
40:     )
41:         internal
42:         virtual
43:         override
44:     {
45:         uint i = 0;
46:         uint n = _totalDistributors;
47:         while (i < n) {
48:             _distributors[i].beforeTokenTransfer(from_, to_, amount_);
49:             i++;
50:         }
51:     }
52: 
75: }
76: 

['6']

2: 
3: pragma solidity =0.8.8;
4: pragma abicoder v2;
5: 
6: import "./modules/UseAccessControl.sol"; // <= FOUND
7: 
8: import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // <= FOUND
9: 
10: import "./lib/LPercentage.sol"; // <= FOUND
11: import "./lib/LLido.sol"; // <= FOUND
12: 
13: import "./modules/Cashier.sol"; // <= FOUND
14: 
15: import "./modules/interfaces/IEarning.sol"; // <= FOUND
16: import "./interfaces/IVoting.sol"; // <= FOUND
17: 
18: contract Voting is UseAccessControl, Cashier {
19: 
20:     modifier onlyInVotingTime(
21:         uint voteId_
22:     )
23:     {
24:         require(voteId_ < _totalVotes, "invalid voteId");
25:         SVote storage vote = _votes[voteId_];
26:         require(vote.startedAt <= block.timestamp, "voting not started");
27:         require(vote.endAt >= block.timestamp, "voting already ended");
28:         _;
29:     }
30: 
31:     struct SVote{
32:         address attacker;
33:         address defender;
34: 
35:         uint aEthValue;
36:         uint dEthValue;
37: 
38:         uint voterPercent;
39:         uint aQuorum;
40: 
41:         uint startedAt;
42:         uint endAt;
43: 
44:         uint attackerPower;
45:         uint defenderPower;
46:         mapping(address => uint) powerOf;
47:         mapping(address => bool) isForAttacker;
48: 
49:         uint totalClaimed;
50:         mapping(address => bool) isClaimed;
51: 
52:         bool isFinalized;
53:         bool isAttackerWon;
54:         uint winVal;
55:         uint winnerPower;
56:         bool isClosed;
57:     }
58: 
59:     event CreateVote(
60:         uint indexed voteId,
61: 
62:         address indexed attacker,
63:         address indexed defender,
64: 
65:         uint aEthValue,

['6']

2: 
3: pragma solidity =0.8.8;
4: pragma abicoder v2;
5: 
6: import "@openzeppelin/contracts/access/Ownable.sol"; // <= FOUND
7: import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // <= FOUND
8: 
9: import "./modules/interfaces/IVester.sol"; // <= FOUND
10: 
11: contract PrivateVester is Ownable{
12:     IVester private _vester;
13:     IERC20 private _token;
14: 
15:     function config(
16:         IVester vester_,
17:         IERC20 token_
18:     )
19:         external
20:         onlyOwner
21:     {
22:         _vester = vester_;
23:         _token = token_;
24:     }
25: 
26:     function _sendTo(
27:         address invester_,
28:         uint amount_,
29:         uint vestingDur_,
30:         uint vestingPercent_,
31:         uint cliff_
32:     )
33:         internal
34:     {
35:         uint vestingA = amount_ * vestingPercent_ / 10000;
36:         uint directA = amount_ - vestingA;
37:         _token.transfer(invester_, directA);
38:         _token.transfer(address(_vester), vestingA);
39:         _vester.lock(invester_, vestingDur_, cliff_);
40:     }
41: 
42:     function transferLock(
43:         address from_,
44:         address to_,
45:         uint amount_
46:     )
47:         external
48:         onlyOwner
49:     {
50:         _vester.transferLock(from_, to_, amount_);
51:         _vester.unlock(to_);
52:     }
53: 
64:     function withdraw(
65:         address dest_,
66:         uint amount_
67:     )
68:         external

['5']

2: 
3: pragma solidity =0.8.8;
4: 
5: import "../../lib/LLocker.sol"; // <= FOUND
6: 
7: import "./ICashier.sol"; // <= FOUND
8: 
9: interface IVester is ICashier {
10:     function initVester(
11:         address accessControl_,
12:         address token_,
13:         address cleanTo_
14:     )
15:         external;
16: 
17:     function lock(
18:         address account_,
19:         uint duration_,
20:         uint cliff_
21:     )
22:         external;
23: 
24:     function transferLock(
25:         address from_,
26:         address to_,
27:         uint amount_
28:     )
29:         external;
30: 
31:     function unlock(
32:         address account_
33:     )
34:         external;
35: 
36:     function currentLockData(
37:         address account_
38:     )
39:         external
40:         view
41:         returns(uint restA, uint restDuration);
42: 
43:     function getUnlockedA(
44:         address account_
45:     )
46:         external
47:         view
48:         returns(uint);
49: 
50:     function getLockData(
51:         address account_
52:     )
53:         external
54:         view
55:         returns(LLocker.SLock memory);
56: }

['5']

2: 
3: pragma solidity =0.8.8;
4: 
5: import "../../lib/LLocker.sol"; // <= FOUND
6: 
7: import "./ICashier.sol"; // <= FOUND
8: 
9: interface ILocker is ICashier {
10:     function initLocker(
11:         address accessControl_,
12:         address token_,
13:         address profileCAddr_,
14:         address penaltyAddress_
15:     )
16:         external;
17: 
18:     function lock(
19:         address account_,
20:         address poolOwner_,
21:         uint duration_
22:     )
23:         external;
24: 
25:     function withdraw(
26:         address account_,
27:         address poolOwner_,
28:         address dest_,
29:         uint amount_,
30:         bool isForced_
31:     )
32:         external;
33: 
34:     function penaltyAddress()
35:         external
36:         view
37:         returns(address);
38: 
39:     function getLockId(
40:         address account_,
41:         address poolOwner_
42:     )
43:         external
44:         pure
45:         returns(bytes32);
46: 
47:     function getLockDataById(
48:         bytes32 lockId_
49:     )
50:         external
51:         view
52:         returns(LLocker.SLock memory);
53: 
54:     function getLockData(
55:         address account_,
56:         address poolOwner_

['6']

2: 
3: pragma solidity =0.8.8;
4: pragma abicoder v2;
5: 
6: import "./modules/UseAccessControl.sol"; // <= FOUND
7: 
8: import "./modules/DToken.sol"; // <= FOUND
9: import "./modules/Distributor.sol"; // <= FOUND
10: 
11: import "./modules/interfaces/IDToken.sol"; // <= FOUND
12: import "./modules/interfaces/IDistributor.sol"; // <= FOUND
13: 
14: import "./interfaces/IPoolFactory.sol"; // <= FOUND
15: 
17: contract PoolFactory is IPoolFactory, UseAccessControl {
18:     event CreatePool(
19:         address indexed owner,
20:         SPool pool
21:     );
22: 
23:     bytes private _dTokenBytecode;
24:     bytes private _distributorBytecode;
25: 
26:     bool private _inPrivateMode = true;
27:     string constant private DIV_TOKEN_NAME = "P2U Dividend Token";
28:     string constant private DIV_TOKEN_SYMBOL = "P2U";
29: 
30:     address private _geth;
31:     address private _dct;
32: 
33:     mapping(address => bool) private _isCreated;
34:     mapping(address => SPool) private _pools;
35: 
36:     function initPoolFactory(
37:         address accessControl_,
38:         address geth_,
39:         address dct_
40:     )
41:         external
42:         initializer
43:     {
44:         initUseAccessControl(accessControl_);
45:         _geth = geth_;
46:         _dct = dct_;
47:         _dTokenBytecode = type(DToken).creationCode;
48:         _distributorBytecode = type(Distributor).creationCode;
49:     }
50: 
51:     function _deploy(
52:         bytes memory bytecode,
53:         uint _salt
54:     )
55:         internal
56:         returns(address addr)
57:     {
58:         assembly {
59:             addr := create2(0, add(bytecode, 0x20), mload(bytecode), _salt)
60:             if iszero(extcodesize(addr)) {
61:                 revert(0, 0)
62:             }
63:         }
64:     }

['6']

2: 
3: pragma solidity =0.8.8;
4: pragma abicoder v2;
5: 
6: import "./modules/UseAccessControl.sol"; // <= FOUND
7: import "./interfaces/IProfile.sol"; // <= FOUND
8: 
9: import "./lib/LPercentage.sol"; // <= FOUND
10: import "./lib/LProfile.sol"; // <= FOUND
11: 
12: contract Profile is IProfile, UseAccessControl {
13:     using LPercentage for *;
14:     using LProfile for *;
15: 
16:     event UpdateSponsor(
17:         address indexed account,
18:         address indexed sponsor,
19:         uint sPercent
20:     );
21: 
22:     event SetSPercent(
23:         address indexed account,
24:         uint sPercent
25:     );
26: 
27:     event UpdateFsOf(
28:         address indexed account,
29:         uint fs
30:     );
31: 
32:     event UpdateBoosterOf(
33:         address indexed account,
34:         uint booster
35:     );
36: 
37:     mapping(address => SProfile) private _profileOf;
38:     uint public _defaultSPercent = 1000; 
39:     uint public _minSPercent = 1000; 
40: 
41:     function initProfile(
42:         address accessControl_
43:     )
44:         external
45:         initializer
46:     {
47:         initUseAccessControl(accessControl_);
48:     }
49: 
50:     function _updateSponsor(
51:         address account_,
52:         address sponsor_
53:     )
54:         internal
55:     {
56:         require(sponsor_ != address(0x0), "invalid sponsor");
57:         SProfile storage profileData = _profileOf[account_];
58:         if (profileData.sponsor == address(0x0)) {
59:             _initDefaultSPercent(account_);

['7']

3: 
5: pragma solidity =0.8.8;
6: 
7: import "./Cashier.sol"; // <= FOUND
8: 
9: import "./Initializable.sol"; // <= FOUND
10: 
11: contract AdaptiveDistributor is Cashier, Initializable {
12:     IERC20 private _dToken;
13: 
14:     event Distribute(
15:         uint amount,
16:         uint regSupply
17:     );
18: 
19:     event Claim(
20:         address indexed holder,
21:         uint reward
22:     );
23: 
24:     event UpdateRegBal(
25:         address indexed holder,
26:         uint regBal
27:     );
28: 
29:     struct SHolder {
30:         uint regBal;
31:         uint lastRpt;
32:         uint lastClaimedAt;
33:     }
34: 
35:     mapping(address => SHolder) public holders;
36: 
37:     uint public rpt;
38:     uint public regSupply;
39: 
40:     uint constant private MFACTOR = 1e18;
41: 
42:     function init(
43:         address dToken_,
44:         address rewardToken_
45:     )
46:         public
47:         initializer
48:     {
49:         _dToken = IERC20(dToken_);
50:         _initCashier(rewardToken_, address(this));
51:     }
52: 
53:     function distribute()
54:         public
55:     {
56:         if (regSupply == 0) return;
57:         uint addedAmount = _cashIn();
58:         if (addedAmount > 0) {
59:             rpt += _cashIn() * MFACTOR / regSupply;

['8']

3: 
5: pragma solidity =0.8.8;
6: pragma abicoder v2;
7: 
8: import "./Cashier.sol"; // <= FOUND
9: 
10: import "../lib/LLocker.sol"; // <= FOUND
11: import "../interfaces/IProfile.sol"; // <= FOUND
12: 
13: import "./UseAccessControl.sol"; // <= FOUND
14: 
15: contract Locker is Cashier, UseAccessControl {
16:     using LLocker for *;
17: 
18:     event UpdateLockData(
19:         address indexed account,
20:         address indexed poolOwner,
21:         LLocker.SLock lockData
22:     );
23: 
24:     event Withdraw(
25:         address indexed account,
26:         address indexed poolOwner,
27:         address dest,
28:         uint amount
29:     );
30: 
31:     event SelfWithdrawn(
32:         address indexed account
33:     );
34: 
35:     event UpdatePenaltyAddress(
36:         address penaltyAddress
37:     );
38: 
39:     mapping(bytes32 => LLocker.SLock) private _lockData;
40:     IProfile private _profileC;
41: 
42:     address private _penaltyAddress;
43: 
44:     function initLocker(
45:         address accessControl_,
46:         address token_,
47:         address profileCAddr_,
48:         address penaltyAddress_,
49:         address cleanTo_
50:     )
51:         public
52:         initializer
53:     {
54:         _initCashier(token_, cleanTo_);
55:         initUseAccessControl(accessControl_);
56:         _profileC = IProfile(profileCAddr_);
57: 
58:         _updatePenaltyAddress(penaltyAddress_);
59:     }
60: 
61:     function lock(
62:         address account_,
63:         address poolOwner_,

['6']

2: 
3: pragma solidity =0.8.8;
4: pragma abicoder v2;
5: 
6: import "./lib/LPercentage.sol"; // <= FOUND
7: import "./lib/LLocker.sol"; // <= FOUND
8: import "./lib/LProfile.sol"; // <= FOUND
9: import "./lib/LHelper.sol"; // <= FOUND
10: import "./lib/LLido.sol"; // <= FOUND
11: 
12: import "./modules/UseAccessControl.sol"; // <= FOUND
13: import "./modules/interfaces/IDToken.sol"; // <= FOUND
14: import "./modules/interfaces/IDistributor.sol"; // <= FOUND
15: import "./modules/interfaces/ILocker.sol"; // <= FOUND
16: import "./modules/interfaces/IEarning.sol"; // <= FOUND
17: 
18: import "./interfaces/IPoolFactory.sol"; // <= FOUND
19: import "./interfaces/IProfile.sol"; // <= FOUND
20: import "./interfaces/IDCT.sol"; // <= FOUND
21: import "./interfaces/IVoting.sol"; // <= FOUND
22: import "./interfaces/IEthSharing.sol"; // <= FOUND
23: import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // <= FOUND
24: 
25: contract Controller is UseAccessControl {
26:     using LPercentage for *;
27:     using LProfile for *;
28: 
29:     modifier tryPublicMint() {
30:         if (block.timestamp - _lastMintAt > _mintingInt) {
31:             _lastMintAt = block.timestamp;
32:             _dct.publicMint();
33:         }
34:         _;
35:     }
36: 
37:     event Stake(
38:         bool indexed isEth,
39:         address indexed poolOwner,
40:         address indexed staker,
41:         uint amount,
42:         uint duration,
43:         uint powerMinted,
44:         bool isFirstStake 
45:     );
46: 
47:     event AdminUpdateConfig(
48:         uint[] values
49:     );
50: 
51:     string constant public VERSION = "DEV";
52: 
53:     IERC20 private _geth;
54:     IDCT private _dct;
55: 
56:     IDistributor private _devTeam;
57:     IPoolFactory private _poolFactory;
58:     IProfile private _profileC;
59: 
60:     ILocker private _eLocker;
61:     IDToken private _eP2PDToken;
62:     
63:     IEarning private _eEarning;
64: 
65:     ILocker private _dLocker;
66:     IDToken private _dP2PDToken;
67:     IDistributor private _dP2PDistributor;
68:     IEarning private _dEarning;
69: 
70:     IVoting private _voting;
71: 
72:     IDistributor private _eDP2PDistributor;

['8']

3: 
5: pragma solidity =0.8.8;
6: pragma abicoder v2;
7: 
8: import "./Cashier.sol"; // <= FOUND
9: 
10: import "./Initializable.sol"; // <= FOUND
11: 
12: import "./interfaces/IDToken.sol"; // <= FOUND
13: 
14: import "./UseAccessControl.sol"; // <= FOUND
15: 
16: contract Distributor is Cashier, Initializable, UseAccessControl {
17:     modifier onlyDToken() {
18:         require(msg.sender == address(_dToken), "onlyDToken");
19:         _;
20:     }
21: 
22:     event SetReward(
23:         address indexed account,
24:         uint prevReward,
25:         uint reward
26:     );
27: 
28:     IDToken private _dToken;
29: 
31:     uint private _ptr;
32:     uint constant private MFACTOR = 10 ** 18;
33: 
34:     mapping(address => int256) private _adjustedRewardOf;
35: 
36:     function initDistributor(
37:         address accessControl_,
38:         address dToken_,
39:         address rewardToken_
40:     )
41:         public
42:         initializer
43:     {
44:         initUseAccessControl(accessControl_);
45:         _dToken = IDToken(dToken_);
46:         _initCashier(rewardToken_, address(this));
47:     }
48: 
49:     function clean()
50:         public
51:         pure
52:         override
53:     {
54:         
55:         revert();
56:     }
57: 
58:     function _distribute()
59:         internal
60:     {
61:         uint totalSupply = _dToken.totalSupply();
62:         if (totalSupply != 0) {
63:             uint amount = _cashIn();
64:             _ptr += amount * MFACTOR / totalSupply;
65:         }

['8']

3: 
5: pragma solidity =0.8.8;
6: pragma abicoder v2;
7: 
8: import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // <= FOUND
9: import "./PERC20.sol"; // <= FOUND
10: import "./Cashier.sol"; // <= FOUND
11: 
12: import "../interfaces/IProfile.sol"; // <= FOUND
13: 
14: contract Earning is Cashier, PERC20 {
15: 
16:     event UpdateMaxEarning(
17:         address indexed account,
18:         uint maxEarning
19:     );
20: 
21:     event ShareCommission(
22:         address indexed account,
23:         address indexed profile,
24:         uint sAmount
25:     );
26: 
27:     event Withdraw(
28:         address indexed account,
29:         uint amount,
30:         address dest
31:     );
32: 
33:     mapping(address => uint) private _sharedA;
34:     mapping(address => uint) private _maxEarningOf;
35:     IProfile private _profileC;
36: 
37:     function initEarning(
38:         address token_,
39:         address profileCAddr_,
40:         address accessControl_,
41:         string memory name_,
42:         string memory symbol_,
43:         address cleanTo_
44:     )
45:         public
46:         virtual
47:         initializer
48:     {
49:         _initCashier(token_, cleanTo_);
50:         _profileC = IProfile(profileCAddr_);
51:         bool inPrivateMode = true;
52:         initPERC20(accessControl_, inPrivateMode, name_, symbol_);
53:     }
54: 
55:     function _updateMaxEarning(
56:         address account_,
57:         uint maxEarning_
58:     )
59:         internal
60:     {
61:         _maxEarningOf[account_] = maxEarning_;
62:         emit UpdateMaxEarning(account_, maxEarning_);

[NonCritical-32] Multiple mappings can be replaced with a single struct mapping

Resolution

Using a single struct mapping in place of multiple defined mappings in a Solidity contract can lead to improved code organization, better readability, and easier maintainability. By consolidating related data into a single struct, developers can create a more cohesive data structure that logically groups together relevant pieces of information, thus reducing redundancy and clutter. This approach simplifies the codebase, making it easier to understand, navigate, and modify. Additionally, it can result in more efficient gas usage when accessing or updating multiple related data points simultaneously.

Num of instances: 3

Findings

Click to show findings

['20']

12: contract Earning is Cashier, PERC20 {
13: 
20:     mapping(address => uint) private _sharedA; // <= FOUND
21:     mapping(address => uint) private _maxEarningOf; // <= FOUND
22:     IProfile private _profileC;
23: 
39: }

['29']

16: contract PoolFactory is IPoolFactory, UseAccessControl {
17:     
19:     bytes private _dTokenBytecode;
20:     bytes private _distributorBytecode;
21: 
22:     bool private _inPrivateMode = true;
23:     string constant private DIV_TOKEN_NAME = "P2U Dividend Token";
24:     string constant private DIV_TOKEN_SYMBOL = "P2U";
25: 
26:     address private _geth;
27:     address private _dct;
28: 
29:     mapping(address => bool) private _isCreated; // <= FOUND
30:     mapping(address => SPool) private _pools; // <= FOUND
31: 
45: }

['35']

17: contract Voting is UseAccessControl, Cashier {
18: 
35:     mapping(uint => SVote) private _votes; // <= FOUND
36:     uint private _totalVotes;
37: 
38:     IERC20 private _votingToken;
39:     IERC20  private _geth;
40: 
41:     IEarning private _eEarning;
42: 
43:     mapping(address => uint) private _defenderEarningFreezedOf; // <= FOUND
44: 
66: }

[NonCritical-33] Unused state variables present

Resolution

If these serve no purpose, they should be safely removed

Num of instances: 2

Findings

Click to show findings

['50']

50: string constant public VERSION = "DEV"; // <= FOUND

['8']

8: uint constant public DEMIE3 = DEMIE2 * DEMI; // <= FOUND

[NonCritical-34] Constants should be on the left side of the comparison

Resolution

Putting constants on the left side of a comparison operator like == or < is a best practice known as "Yoda conditions", which can help prevent accidental assignment instead of comparison. In some programming languages, if a variable is mistakenly put on the left with a single = instead of ==, it assigns the constant's value to the variable without any compiler error. However, doing this with the constant on the left would generate an error, as constants cannot be assigned values. Although Solidity's static typing system prevents accidental assignments within conditionals, adopting this practice enhances code readability and consistency, especially when developers are working across multiple languages that support this convention.

Num of instances: 23

Findings

Click to show findings

['82']

82:        if (isMintingFinished || _lastMintAt == 0)  // <= FOUND

['93']

93:         if (mintingA == 0)  // <= FOUND

['60']

60:         if (totalSupply != 0)  // <= FOUND

['79']

79:         if (amount == 0)  // <= FOUND

['79']

79:         if (amount == 0)  // <= FOUND

['38']

38:         if (lockTime_ == 0)  // <= FOUND

['32']

32:         if (max == 0)  // <= FOUND

['205']

205:        if (wethA_ == 0)  // <= FOUND

['241']

241:        if (wstethA_ == 0)  // <= FOUND

['50']

50:        if (lockData_.amount == 0)  // <= FOUND

['59']

59:         if (rd == 0)  // <= FOUND

['227']

227:         if (power == 0)  // <= FOUND

['85']

85:            if (regSupply == 0)  // <= FOUND

['56']

56:         if (addedAmount > 0)  // <= FOUND

['96']

96:         if (reward > 0)  // <= FOUND

['404']

404:        if (minWstethA_ > 0)  // <= FOUND

['407']

407:         if (wstethA_ > 0)  // <= FOUND

['146']

146:         if (amount > 0)  // <= FOUND

['86']

86:         if (sAmount > 0)  // <= FOUND

['146']

146:         if (amount > 0)  // <= FOUND

['146']

146:         if (amount > 0)  // <= FOUND

['89']

89:         if (toTransferA > 0)  // <= FOUND

['199']

199:         if (power > 0)  // <= FOUND

[NonCritical-35] Use a single file for system wide constants

Num of instances: 5

Findings

Click to show findings

['25']

9: contract AdaptiveDistributor is Cashier, Initializable {
10:     IERC20 private _dToken;
11: 
25:     uint constant private MFACTOR = 1e18; // <= FOUND

['34']

24: contract Controller is UseAccessControl {
25:     using LPercentage for *;
26:     using LProfile for *;
34:     string constant public VERSION = "DEV"; // <= FOUND

['14']

9: contract DCT is ERC20, UseAccessControl {
10:     uint private _tps = 7 ether;
11: 
14:     uint constant public HALVING_INTERVAL = 7 days; // <= FOUND

22:     uint constant public MAX_SUPPLY = 3111666666 ether; // <= FOUND

['23']

14: contract Distributor is Cashier, Initializable, UseAccessControl {
15: 
19:     IDToken private _dToken;
23:     uint constant private MFACTOR = 10 ** 18; // <= FOUND

['23']

16: contract PoolFactory is IPoolFactory, UseAccessControl {
17:     
19:     bytes private _dTokenBytecode;
23:     string constant private DIV_TOKEN_NAME = "P2U Dividend Token"; // <= FOUND

24:     string constant private DIV_TOKEN_SYMBOL = "P2U"; // <= FOUND

[NonCritical-36] Return values of transfer()/transferFrom() not checked

Resolution

In Solidity, it's crucial to check the return value of .transfer and .transferFrom methods because not all ERC20 and ERC721 token implementations revert on failure. Notably, If these methods fail silently and the contract doesn't verify their return value, the contract might continue execution as if the tokens were transferred successfully, leading to incorrect balances, loss of funds, or other unexpected behaviors. Therefore, the return values of these methods should always be checked, ensuring a failed token transfer operation correctly halts the execution of the contract.

Num of instances: 14

Findings

Click to show findings

['60']

60:     function _cashOut(
61:         address to_,
62:         uint amount_
63:     )
64:         internal
65:     {
66:         _token.transfer(to_, amount_); // <= FOUND
67:         _updateBalance();
68:     }

['95']

95:     function clean()
96:         public
97:         virtual
98:     {
99:         require(_isCleanEnabled, "unable to clean");
100:         uint currentBal = currentBalance();
101:         if (currentBal > _lastestBalance) {
102:             uint amount = currentBal - _lastestBalance;
103:             _token.transfer(_cleanTo, amount); // <= FOUND
104:             emit Clean(amount);
105:         }
106:         _updateBalance();
107:     }

['234']

234:     function _shareDevTeam(
235:         uint amount_
236:     )
237:         internal
238:     {
239:         _geth.transfer(address(_devTeam), amount_); // <= FOUND
240:     }

['243']

243:     function _sharePoolOwner(
244:         uint amount_,
245:         address payable poolOwner_
246:     )
247:         internal
248:     {
249:         _eEarning.clean();
250: 
251:         _geth.transfer(address(_eEarning), amount_); // <= FOUND
252: 
253:         _eEarning.update(poolOwner_, true);
254:     }

['257']

257:     function _sharePoolUser(
258:         uint amount_,
259:         address payable poolOwner_
260:     )
261:         internal
262:     {
263:         IPoolFactory.SPool memory pool = _poolFactory.getPool(poolOwner_);
264:         _geth.transfer(pool.ethDistributor, amount_); // <= FOUND
265:     }

['267']

267:     function _lock(
268:         bool isEth_,
269:         address payable poolOwner_,
270:         uint duration_
271:     )
272:         internal
273:         returns (uint)
274:     {
275: 
276:         if (isEth_) {
277:             uint value = _geth.balanceOf(address(this));
278:             _eLocker.clean();
279:             _geth.transfer(address(_eLocker), value); // <= FOUND
280:             _eLocker.lock(msg.sender, poolOwner_, duration_);
281: 
282:             return value;
283:         } else {
284:             uint value = _dct.balanceOf(address(this));
285:             _dLocker.clean();
286:             _dct.transfer(address(_dLocker), value);
287:             _dLocker.lock(msg.sender, poolOwner_, duration_);
288: 
289:             return value;
290:         }
291:     }

['398']

398:     function _prepareWsteth(
399:         uint minWstethA_,
400:         uint wstethA_
401:     )
402:         internal
403:     {
404:         if (minWstethA_ > 0) {
405:             LLido.allToWsteth(minWstethA_);
406:         }
407:         if (wstethA_ > 0) {
408:             _geth.transferFrom(msg.sender, address(this), wstethA_); // <= FOUND
409:         }
410:     }

['429']

429:     function dctStake(
430:         uint amount_,
431:         address payable poolOwner_,
432:         uint duration_
433:     )
434:         public
435:         payable
436:         tryPublicMint
437:     {
438:         _dct.transferFrom(msg.sender, address(this), amount_);
439:         uint taxA = LPercentage.getPercentA(amount_, _dctTaxPercent);
440:         _dct.transfer(address(0xdead), taxA); // <= FOUND
441:         bool isEth = false;
442:         
443:         uint poolConfigCode = 0;
444:         _stake(isEth, msg.sender, poolOwner_, duration_, 0, poolConfigCode);
445:     }

['478']

478:     function earningPulls(
479:         address account_,
480:         address[] memory poolOwners_,
481:         address bountyPullerTo_
482:     )
483:         public
484:         tryPublicMint
485:     {
486:         _dEarning.clean();
487:         _eEarning.clean();
488: 
489:         
490:         _dP2PDistributor.distribute();
491: 
492:         
493:         
494: 
495:         
496:         
497: 
498:         _distributorClaimFor(address(_eDP2PDistributor), account_, address(this));
499:         _distributorClaimFor(address(_dDP2PDistributor), account_, address(_dEarning));
500: 
501:         for(uint i = 0; i < poolOwners_.length; i++) {
502:             _earningPull(account_, poolOwners_[i]);
503:         }
504: 
505:         if (bountyPullerTo_ == account_) {
506:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this))); // <= FOUND
507:         } else {
508:             uint256 amountForPuller = _geth.balanceOf(address(this)) * _bountyPullEarningPercent / LPercentage.DEMI;
509: 
510:             LLido.sellWsteth(amountForPuller);
511:             LLido.wethToEth();
512:             payable(bountyPullerTo_).transfer(address(this).balance);
513: 
514:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this))); // <= FOUND
515:         }
516: 
517:         _dEarning.update(account_, true);
518:         _eEarning.update(account_, true);
519: 
520:         _reCalFs(account_);
521:     }

['543']

543:     function lockWithdraw(
544:         bool isEth_,
545:         address payable poolOwner_,
546:         uint amount_,
547:         address payable dest_,
548:         bool isForced_,
549:         uint minEthA_
550:     )
551:         public
552:         tryPublicMint
553:     {
554:         address account = msg.sender;
555:         ILocker locker = isEth_ ? _eLocker : _dLocker;
556: 
557:         LLocker.SLock memory oldLockData = locker.getLockData(account, poolOwner_);
558:         locker.withdraw(account, poolOwner_, address(this), amount_, isForced_);
559:         uint restAmount = oldLockData.amount - amount_;
560: 
561:         
562:         if (isEth_) {
563:             require(restAmount == 0 || restAmount >= _minStakeETHAmount, "rest amount too small");
564: 
565:             IPoolFactory.SPool memory pool = _poolFactory.getPool(poolOwner_);
566:             IDToken p2UDtoken = IDToken(pool.dToken);
567:             uint burnedPower = LHelper.calBurnStakingPower(p2UDtoken.balanceOf(account), amount_, oldLockData.amount);
568:             p2UDtoken.burn(account, burnedPower);
569: 
570:             _reCalEP2PDBalance(poolOwner_);
571:             LLido.allToEth(minEthA_);
572:             
573:             dest_.transfer(address(this).balance);
574:         } else {
575:             require(restAmount == 0 || restAmount >= _minStakeDCTAmount, "rest amount too small");
576: 
577:             uint burnedPower = LHelper.calBurnStakingPower(_dP2PDToken.balanceOf(poolOwner_), amount_, oldLockData.amount);
578:             _dP2PDToken.burn(poolOwner_, burnedPower);
579: 
580:             _reCalBooster(poolOwner_);
581:             _reCalEP2PDBalance(poolOwner_);
582: 
583:             _dct.transfer(dest_, _dct.balanceOf(address(this))); // <= FOUND
584:         }
585:     }

['615']

615:     function earningWithdraw(
616:         bool isEth_,
617:         uint amount_,
618:         address payable dest_,
619:         uint minEthA_
620:         
621:     )
622:         public
623:     {
624:         address account = msg.sender;
625:         
626:         IEarning earning = isEth_ ? _eEarning : _dEarning;
627: 
628:         earning.withdraw(account, amount_, address(this));
629:         
630:         if (isEth_) {
631:             _reCalFs(account);
632:             if (dest_ != address(this)) {
633:                 LLido.allToEth(minEthA_);
634:                 dest_.transfer(address(this).balance);
635:             }
636:         } else {
637:             _dct.transfer(dest_, _dct.balanceOf(address(this))); // <= FOUND
638:         }
639:     }

['641']

641:     function createVote(
642:         address defender_,
643:         uint dEthValue_,
644:         uint voterPercent_,
645:         uint freezeDuration_,
646:         uint minWstethA_,
647:         uint wstethA_
648:     )
649:         external
650:         payable
651:     {
652:         require(_isPausedAttack == 0, "paused");
653: 
654:         address attacker = msg.sender;
655:         
656:         _prepareWsteth(minWstethA_, wstethA_);
657:         uint aEthValue = _geth.balanceOf(address(this));
658: 
659:         require(defender_ != address(_devTeam));
660:         require(dEthValue_ >= _minDefenderFund, "dEthValue_ too small");
661:         require(voterPercent_ <= _maxVoterPercent, "voterPercent_ too high");
662:         require(freezeDuration_ >= _minFreezeDuration && freezeDuration_ <= _maxFreezeDuration, "freezeDuration_ invalid");
663:         require(aEthValue <= dEthValue_ && aEthValue * LPercentage.DEMI / dEthValue_ >= _minAttackerFundRate, "aEthValue invalid");
664: 
665:         uint aQuorum = LHelper.calAQuorum(
666:             aEthValue,
667:             dEthValue_,
668:             voterPercent_,
669:             freezeDuration_,
670:             _freezeDurationUnit
671:         );
672: 
673:         _voting.clean();
674:         _eEarning.withdraw(defender_, dEthValue_, address(_voting));
675:         _geth.transfer(address(_voting), aEthValue); // <= FOUND
676: 
677:         _dct.transferFrom(attacker, address(0xdead), _attackFee);
678: 
679:         _voting.createVote(
680:             attacker,
681:             defender_,
682:             aEthValue,
683:             dEthValue_,
684:             voterPercent_,
685:             aQuorum,
686:             block.timestamp,
687:             block.timestamp + freezeDuration_
688:         );
689: 
690:         _reCalFs(defender_);
691:     }

['25']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     {
34:         uint vestingA = amount_ * vestingPercent_ / 10000;
35:         uint directA = amount_ - vestingA;
36:         _token.transfer(invester_, directA); // <= FOUND
37:         _token.transfer(address(_vester), vestingA);
38:         _vester.lock(invester_, vestingDur_, cliff_);
39:     }

['63']

63:     function withdraw(
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     {
70:         _token.transfer(dest_, amount_); // <= FOUND
71:     }

[NonCritical-37] Consider using modifiers for address control

Resolution

Modifiers in Solidity can improve code readability and modularity by encapsulating repetitive checks, such as address validity checks, into a reusable construct. For example, an onlyOwner modifier can be used to replace repetitive require(msg.sender == owner) checks across several functions, reducing code redundancy and enhancing maintainability. To implement, define a modifier with the check, then apply the modifier to relevant functions.

Num of instances: 1

Findings

Click to show findings

['55']

55:     function _beforeTokenTransfer(
56:         address from_,
57:         address,
58:         uint256
59:     )
60:         internal
61:         virtual
62:         override
63:     {
64:         if (msg.sender != address(_vester)) { // <= FOUND
65:            _vester.unlock(from_);
66:         }
67:     }

[NonCritical-38] Use of non-named numeric constants

Resolution

Magic numbers should be avoided in Solidity code to enhance readability, maintainability, and reduce the likelihood of errors. Magic numbers are hard-coded values with no clear meaning or context, which can create confusion and make the code harder to understand for developers. Using well-defined constants or variables with descriptive names instead of magic numbers not only clarifies the purpose and significance of the value but also simplifies code updates and modifications.

Num of instances: 6

Findings

Click to show findings

['34']

34:         uint vestingA = amount_ * vestingPercent_ / 10000; // <= FOUND

['103']

103:             _tps = _tps / 2; // <= FOUND

['171']

171:         uint wethA = amount / 2; // <= FOUND

['77']

77:         uint mFactor = isPoolOwner_ ? 2 * LPercentage.DEMI - fs_ : fs_; // <= FOUND

['86']

86:         uint x = lockTime_ * LPercentage.DEMI / 30 days; // <= FOUND

['99']

99:         uint rs = (1300 * x / LPercentage.DEMI) + 8800; // <= FOUND

[NonCritical-39] Long powers of ten should use scientific notation 1eX

Resolution

A large number such as 1000000 is far more readable as 1e6, this will help prevent unintended bugs in the code

Num of instances: 4

Findings

Click to show findings

['6']

6:     uint constant public DEMI = 10000; // <= FOUND

['34']

34:         uint vestingA = amount_ * vestingPercent_ / 10000; // <= FOUND

['37']

37:     uint public _defaultSPercent = 1000;  // <= FOUND

['38']

38:     uint public _minSPercent = 1000;  // <= FOUND

[NonCritical-40] Redundant else statement

Num of instances: 2

Findings

Click to show findings

[]

267:     function _lock(
268:         bool isEth_,
269:         address payable poolOwner_,
270:         uint duration_
271:     )
272:         internal
273:         returns (uint)
274:     {
275: 
276:         if (isEth_) {
277:             uint value = _geth.balanceOf(address(this));
278:             _eLocker.clean();
279:             _geth.transfer(address(_eLocker), value);
280:             _eLocker.lock(msg.sender, poolOwner_, duration_);
281: 
282:             return value;
283:         } else {
284:             uint value = _dct.balanceOf(address(this));
285:             _dLocker.clean();
286:             _dct.transfer(address(_dLocker), value);
287:             _dLocker.lock(msg.sender, poolOwner_, duration_);
288: 
289:             return value;
290:         }
291:     }

[]

25:     function restDuration(
26:         SLock memory lockData_
27:     )
28:         internal
29:         view
30:         returns(uint)
31:     {
32:         if (lockData_.startedAt > block.timestamp) {
33:             return lockData_.duration + lockData_.startedAt - block.timestamp;
34:         }
35:         uint pastTime = block.timestamp - lockData_.startedAt;
36:         if (pastTime < lockData_.duration) {
37:             return lockData_.duration - pastTime;
38:         } else {
39:             return 0;
40:         }
41:     }

[NonCritical-41] Inconsistent usage of int/uint with int256/uint256 in contract/abstract/library/interface

Resolution

Use uint256/int256 consistently in place of uint/int to prevent ambiguity rather than using both within the same contract/abstract/library/interface

Num of instances: 8

Findings

Click to show findings

['9']

9: contract DCT is ERC20, UseAccessControl {
10:     uint private _tps = 7 ether; // <= FOUND
12:     uint private _lastMintAt; // <= FOUND

13:     uint private _lastHalved; // <= FOUND

14:     uint constant public HALVING_INTERVAL = 7 days; // <= FOUND

18:     uint private _athBalance; // <= FOUND

22:     uint constant public MAX_SUPPLY = 3111666666 ether; // <= FOUND

31:         uint256 premineAmount_, // <= FOUND

58:         uint256 // <= FOUND

72:         returns(uint) // <= FOUND

80:         returns(uint) // <= FOUND

85:         uint pastTime = block.timestamp - _lastMintAt; // <= FOUND

92:         uint mintingA = pendingA(); // <= FOUND

111:         returns(uint) // <= FOUND

119:         returns(uint) // <= FOUND

140:     function balanceOf(address account_) public view override returns (uint256) { // <= FOUND

['10']

10: contract DToken is PERC20 {
11:     mapping(uint => IDistributor) private _distributors; // <= FOUND
12:     uint private _totalDistributors; // <= FOUND

26:         uint i = 0; // <= FOUND

27:         uint n = _totalDistributors; // <= FOUND

37:         uint256 amount_ // <= FOUND

43:         uint i = 0; // <= FOUND

44:         uint n = _totalDistributors; // <= FOUND

['14']

14: contract Distributor is Cashier, Initializable, UseAccessControl {
15:     modifier onlyDToken() {
22:         uint prevReward, // <= FOUND

23:         uint reward // <= FOUND

29:     uint private _ptr; // <= FOUND

30:     uint constant private MFACTOR = 10 ** 18; // <= FOUND

59:         uint totalSupply = _dToken.totalSupply(); // <= FOUND

61:             uint amount = _cashIn(); // <= FOUND

69:         uint256 amount_ // <= FOUND

79:         uint reward; // <= FOUND

84:             uint nextBalance = _dToken.balanceOf(account) - amount_; // <= FOUND

91:             uint nextBalance = _dToken.balanceOf(account) + amount_; // <= FOUND

98:         uint prevReward, // <= FOUND

99:         uint reward_, // <= FOUND

100:         uint dTokenBalance_ // <= FOUND

115:         uint ptr_, // <= FOUND

116:         uint dTokenBalance_, // <= FOUND

121:         returns(uint) // <= FOUND

133:         returns(uint) // <= FOUND

145:         uint amount = rewardOf(account_); // <= FOUND

['47']

47: contract PERC20 is ERC20, UseAccessControl, ATHBalance {
48:     event SetInPrivateMode(
104:         uint256 // <= FOUND

136:     function transfer(address to, uint256 amount) public virtual override returns (bool) { // <= FOUND

141:     function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { // <= FOUND

157:         uint amount_ // <= FOUND

167:         uint amount_ // <= FOUND

['16']

16: contract PoolFactory is IPoolFactory, UseAccessControl {
17:     event CreatePool(
52:         uint _salt // <= FOUND

73:         uint salt = uint256(uint160(owner_)); // <= FOUND

['7']

7: interface INonfungiblePositionManager {
8:     struct MintParams {
14:         uint amount0Desired; // <= FOUND

15:         uint amount1Desired; // <= FOUND

16:         uint amount0Min; // <= FOUND

17:         uint amount1Min; // <= FOUND

19:         uint deadline; // <= FOUND

22:     function positions(uint256 tokenId) // <= FOUND

34:             uint256 feeGrowthInside0LastX128, // <= FOUND

35:             uint256 feeGrowthInside1LastX128, // <= FOUND

40:     function burn(uint256 tokenId) external payable; // <= FOUND

47:         returns (uint tokenId, uint128 liquidity, uint amount0, uint amount1); // <= FOUND

50:         uint tokenId; // <= FOUND

51:         uint amount0Desired; // <= FOUND

52:         uint amount1Desired; // <= FOUND

53:         uint amount0Min; // <= FOUND

54:         uint amount1Min; // <= FOUND

55:         uint deadline; // <= FOUND

60:     ) external payable returns (uint128 liquidity, uint amount0, uint amount1); // <= FOUND

63:         uint tokenId; // <= FOUND

65:         uint amount0Min; // <= FOUND

66:         uint amount1Min; // <= FOUND

67:         uint deadline; // <= FOUND

72:     ) external payable returns (uint amount0, uint amount1); // <= FOUND

75:         uint tokenId; // <= FOUND

83:     ) external payable returns (uint amount0, uint amount1); // <= FOUND

['6']

6: interface IDCT is IPERC20 {
7:     function HALVING_INTERVAL()
10:         returns(uint); // <= FOUND

16:         uint256 premineAmount_, // <= FOUND

24:         returns(uint); // <= FOUND

29:         returns(uint); // <= FOUND

37:         returns(uint); // <= FOUND

42:         returns(uint); // <= FOUND

['6']

6: interface IDistributor is ICashier {
7:     function initDistributor(
17:         uint256 amount_ // <= FOUND

29:         returns(uint); // <= FOUND

[NonCritical-42] Employ Explicit Casting to Bytes or Bytes32 for Enhanced Code Clarity and Meaning

Resolution

Smart contracts are complex entities, and clarity in their operations is fundamental to ensure that they function as intended. Casting a single argument instead of utilizing 'abi.encodePacked()' improves the transparency of the operation. It elucidates the intent of the code, reducing ambiguity and making it easier for auditors and developers to understand the code’s purpose. Such practices promote readability and maintainability, thus reducing the likelihood of errors and misunderstandings. Therefore, it's recommended to employ explicit casts for single arguments where possible, to increase the contract's comprehensibility and ensure a smoother review process.

Num of instances: 1

Findings

Click to show findings

['22']

14:     function getLockId(
15:         address account_,
16:         address poolOwner_
17:     )
18:         internal
19:         pure
20:         returns(bytes32)
21:     {
22:         return keccak256(abi.encodePacked(account_, poolOwner_)); // <= FOUND
23:     }

[NonCritical-43] Unused structs present

Resolution

If these serve no purpose, they should be safely removed

Num of instances: 1

Findings

Click to show findings

['105']

105:     struct ExactInputParams { // <= FOUND
106:         bytes path;
107:         address recipient;
108:         uint deadline;
109:         uint amountIn;
110:         uint amountOutMinimum;
111:     }

[NonCritical-44] Non-assembly method available

Resolution

The codebase features instances where low-level assembly is used to access properties like chain ID, external contract size, and external contract hash. However, since Solidity provides native high-level constructs to access these properties, it is recommended to use those for better readability, maintainability, and reduced complexity. For example, assembly{ id := chainid() } can be replaced with uint256 id = block.chainid, assembly { size := extcodesize() } can be substituted with uint256 size = address().code.length, and assembly { hash := extcodehash() } can be transformed into bytes32 hash = address().codehash. The usage of inline assembly can often flag the project as more complex by automated analysis tools and therefore, should be avoided where high-level constructs can achieve the same functionality.

Num of instances: 2

Findings

Click to show findings

['137']

136:     assembly {
137:         size := extcodesize(_addr) // <= FOUND
138:     }

['59']

57:        assembly {
58:             addr := create2(0, add(bytecode, 0x20), mload(bytecode), _salt)
59:             if iszero(extcodesize(addr)) { // <= FOUND
60:                 revert(0, 0)
61:             }
62:         }

[NonCritical-45] Empty bytes check is missing

Resolution

When developing smart contracts in Solidity, it's crucial to validate the inputs of your functions. This includes ensuring that the bytes parameters are not empty, especially when they represent crucial data such as addresses, identifiers, or raw data that the contract needs to process.

Missing empty bytes checks can lead to unexpected behaviour in your contract. For instance, certain operations might fail, produce incorrect results, or consume unnecessary gas when performed with empty bytes. Moreover, missing input validation can potentially expose your contract to malicious activity, including exploitation of unhandled edge cases.

To mitigate these issues, always validate that bytes parameters are not empty when the logic of your contract requires it.

Num of instances: 1

Findings

Click to show findings

['158']

158:     function getLockDataById(
159:         bytes32 lockId_
160:     )
161:         external
162:         view
163:         returns(LLocker.SLock memory)
164:     {
165:         return _lockData[lockId_];
166:     }

[NonCritical-46] No equate comparison checks between to and from address parameters

Resolution

The function lacks a standard check: it does not validate if the 'to' and 'from' addresses are identical. This omission can lead to unintended outcomes if the same address is used in both parameters. To rectify this, we recommend implementing a comparison check at the beginning of the function. In the context of Solidity, the command require(to != from, "To and From addresses can't be the same"); could be utilized. This addition will generate an error if the 'to' and 'from' addresses are the same, thereby fortifying the function's robustness and security.

Num of instances: 4

Findings

Click to show findings

['34']

34:     function _beforeTokenTransfer(
35:         address from_, // <= FOUND
36:         address to_, // <= FOUND
37:         uint256 amount_
38:     )
39:         internal
40:         virtual
41:         override
42:     {
43:         uint i = 0;
44:         uint n = _totalDistributors;
45:         while (i < n) {
46:             _distributors[i].beforeTokenTransfer(from_, to_, amount_);
47:             i++;
48:         }
49:     }

['141']

141:     function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { // <= FOUND
142:         require(!_inPrivateMode, "_inPrivateMode");
143:         return super.transferFrom(from, to , amount);
144:     }

['41']

41:     function transferLock(
42:         address from_, // <= FOUND
43:         address to_, // <= FOUND
44:         uint amount_
45:     )
46:         external
47:         onlyOwner
48:     {
49:         _vester.transferLock(from_, to_, amount_);
50:         _vester.unlock(to_);
51:     }

['52']

52:     function transferLock(
53:         address from_, // <= FOUND
54:         address to_, // <= FOUND
55:         uint amount_
56:     )
57:         external
58:         onlyAdmin
59:     {
60:         unlock(from_);
61:         unlock(to_);
62:         LLocker.SLock storage lockData;
63:         lockData = _lockData[from_];
64:         lockData.amount -= amount_;
65:         uint duration = lockData.duration;
66:         emit UpdateLockData(from_, lockData);
67:         lockData = _lockData[to_];
68:         lockData.amount += amount_;
69:         if (lockData.duration < duration) {
70:             lockData.duration = duration;
71:         }
72:         emit UpdateLockData(to_, lockData);
73:     }

[NonCritical-47] Top level declarations should be separated by two blank lines

Num of instances: 58

Findings

Click to show findings

['73']

73:     } // <= FOUND
74: 
75:     function unlock( // <= FOUND

['121']

121:     } // <= FOUND
122: 
123:     function getUnlockedA( // <= FOUND

['133']

133:     } // <= FOUND
134: 
135:     function getLockData( // <= FOUND

['208']

208:     } // <= FOUND
209: 
210:     function _addVoter( // <= FOUND

['295']

295:     } // <= FOUND
296: 
297:     function claimFor( // <= FOUND

['325']

325:     } // <= FOUND
326: 
327:     function closeVote( // <= FOUND

['44']

44:     } // <= FOUND
45: 
46:     function start() // <= FOUND

['32']

32:     } // <= FOUND
33: 
34:     function _beforeTokenTransfer( // <= FOUND

['122']

122:     } // <= FOUND
123: 
124:     function rewardPool() // <= FOUND

['23']

23:     } // <= FOUND
24: 
25:     function _sendTo( // <= FOUND

['71']

71:     } // <= FOUND
72: 
73:     function sendTos( // <= FOUND

['130']

130:     } // <= FOUND
131: 
132:     function vester() // <= FOUND

['12']

12:     } // <= FOUND
13: 
14:     function updateSponsor( // <= FOUND

['112']

112:     } // <= FOUND
113: 
114:     function isCreated( // <= FOUND

['90']

90:     } // <= FOUND
91: 
92:     function _initDefaultSPercent( // <= FOUND

['120']

120:     } // <= FOUND
121: 
122:     function updateFsOf( // <= FOUND

['132']

132:     } // <= FOUND
133: 
134:     function updateBoosterOf( // <= FOUND

['144']

144:     } // <= FOUND
145: 
146:     function profileOf( // <= FOUND

['171']

171:     } // <= FOUND
172: 
173:     function fsOf( // <= FOUND

['108']

108:     } // <= FOUND
109: 
110:     function claimableOf( // <= FOUND

['21']

21:     } // <= FOUND
22: 
23:     function calMintStakingPower( // <= FOUND

['33']

33:     } // <= FOUND
34: 
35:     function lock( // <= FOUND

['156']

156:     } // <= FOUND
157: 
158:     function getLockDataById( // <= FOUND

['291']

291:     } // <= FOUND
292: 
293:     function _stake( // <= FOUND

['410']

410:     } // <= FOUND
411: 
412:     function ethStake( // <= FOUND

['476']

476:     } // <= FOUND
477: 
478:     function earningPulls( // <= FOUND

['613']

613:     } // <= FOUND
614: 
615:     function earningWithdraw( // <= FOUND

['691']

691:     } // <= FOUND
692: 
693:     function votingClaimFor( // <= FOUND

['36']

36:     } // <= FOUND
37: 
38:     function _removeAdmin( // <= FOUND

['83']

83:     } // <= FOUND
84: 
85:     function unpause() // <= FOUND

['103']

103:     } // <= FOUND
104:     function isAdmin( // <= FOUND
105:         address account_

['112']

112:     } // <= FOUND
113: 
114:     function isPaused() // <= FOUND

['33']

33:     } // <= FOUND
34: 
35:     function _setCleanTo( // <= FOUND

['107']

107:     } // <= FOUND
108: 
109:     function cleanTo() // <= FOUND

['18']

18:     } // <= FOUND
19: 
20:     function getPercentA( // <= FOUND

['57']

57:     } // <= FOUND
58: 
59:     function getCode( // <= FOUND

['120']

120:     } // <= FOUND
121: 
122:     function configSystem( // <= FOUND

['195']

195:     } // <= FOUND
196: 
197:     function getSysExcPart( // <= FOUND

['234']

234:     } // <= FOUND
235: 
236:     function getLockedPart( // <= FOUND

['249']

249:     } // <= FOUND
250: 
251:     function getSharingParts( // <= FOUND

['51']

51:     } // <= FOUND
52: 
53:     function _updateMaxEarning( // <= FOUND

['132']

132:     } // <= FOUND
133: 
134:     function maxEarningOf( // <= FOUND

['145']

145:     } // <= FOUND
146: 
147:     function getLockId( // <= FOUND

['66']

66:     } // <= FOUND
67: 
68:     function isUnlocked( // <= FOUND

['81']

81:     } // <= FOUND
82: 
83:     function calDuration( // <= FOUND

['80']

80:     } // <= FOUND
81: 
82:     function activeAthRecord() // <= FOUND

['113']

113:     } // <= FOUND
114: 
115:     function name() public view virtual override returns (string memory) { // <= FOUND

['125']

125:     } // <= FOUND
126: 
127:     function _setInPrivateMode( // <= FOUND

['134']

134:     } // <= FOUND
135: 
136:     function transfer(address to, uint256 amount) public virtual override returns (bool) { // <= FOUND

['81']

81:     } // <= FOUND
82: 
83:     function approveAdmin( // <= FOUND

['93']

93:     } // <= FOUND
94: 
95:     function revokeAdmin( // <= FOUND

['105']

105:     } // <= FOUND
106: 
107:     function isApprovedAdmin( // <= FOUND

['20']

20:     } // <= FOUND
21: 
22:     function positions(uint256 tokenId) // <= FOUND

['56']

56:     } // <= FOUND
57: 
58:     function increaseLiquidity( // <= FOUND

['68']

68:     } // <= FOUND
69: 
70:     function decreaseLiquidity( // <= FOUND

['224']

224:     } // <= FOUND
225: 
226:     function allToEth( // <= FOUND

['347']

347:     } // <= FOUND
348: 
349:     function getLiquidity( // <= FOUND

['57']

57:     } // <= FOUND
58: 
59:     modifier onlyApprovedAdmin( // <= FOUND

[NonCritical-48] Revert should be used on some functions instead of return

Resolution

Using the revert statement instead of return in certain functions is essential for contract integrity in Solidity. While return merely exits the function and returns a value, revert undoes all changes made in the current function and throughout all its subsequent calls. In scenarios where a failure condition is met, such as incorrect inputs or an unauthorized access attempt, using revert ensures that the contract's state is not altered in an unintended way. This preserves consistency and helps prevent logical errors. It also makes the code's intention clear, simplifying debugging and maintenance.

Num of instances: 1

Findings

Click to show findings

['150']

150:     function initPoolConfig(
151:         address poolOwner_
152:     )
153:         external
154:         onlyAdmin
155:         returns(bool)
156:     {
157:         SPoolConfig storage poolConfig = poolConfigOf[poolOwner_];
158:         if (poolConfig.isInitialized) { // <= FOUND
159:             return false; // <= FOUND
160:         }
161:         _configPool(poolOwner_, defaultOwnerPercent, defaultUserPercent);
162:         return true;
163:     }

[NonCritical-49] No access control on receive/payable fallback

Resolution

Without access control on receive/payable fallback functions in a contract, anyone can send Ether (ETH) to the contract's address. If there's no way to withdraw those funds defined within the contract, any Ether sent, whether intentionally or by mistake, will be permanently stuck. This could lead to unintended loss of funds. Implementing proper access control ensures that only authorized addresses can interact with these functions. Resolution could involve adding access control mechanisms, like Ownable or specific permission requirements, or creating a withdrawal function accessible only to the contract's owner, thus preventing unintentional loss of funds.

Num of instances: 1

Findings

Click to show findings

['117']

117:     receive() external payable {} // <= FOUND

[NonCritical-50] Unused events present

Resolution

If these serve no purpose, they should be safely removed

Num of instances: 1

Findings

Click to show findings

['29']

29: event SelfWithdrawn( // <= FOUND
30:         address indexed account
31:     );

[NonCritical-51] Missing events in sensitive functions

Resolution

Sensitive setter functions in smart contracts often alter critical state variables. Without events emitted in these functions, external observers or dApps cannot easily track or react to these state changes. Missing events can obscure contract activity, hampering transparency and making integration more challenging. To resolve this, incorporate appropriate event emissions within these functions. Events offer an efficient way to log crucial changes, aiding in real-time tracking and post-transaction verification.

Num of instances: 10

Findings

Click to show findings

['146']

146:     function setInPrivateMode( // <= FOUND
147:         bool inPrivateMode_
148:     )
149:         external
150:         onlyOwner
151:     {
152:         _setInPrivateMode(inPrivateMode_);
153:     }

['102']

102:     function setDefaultSPercentConfig( // <= FOUND
103:         uint sPercent_
104:     )
105:         external
106:         onlyAdmin
107:     {
108:         LPercentage.validatePercent(sPercent_);
109:         _defaultSPercent = sPercent_;
110:     }

['112']

112:     function setMinSPercentConfig( // <= FOUND
113:         uint sPercent_
114:     )
115:         external
116:         onlyAdmin
117:     {
118:         LPercentage.validatePercent(sPercent_);
119:         _minSPercent = sPercent_;
120:     }

['63']

63:     function updateMaxEarning( // <= FOUND
64:         address account_,
65:         uint maxEarning_
66:     )
67:         external
68:         onlyAdmin
69:     {
70:         _updateMaxEarning(account_, maxEarning_);
71:     }

['96']

96:     function update( // <= FOUND
97:         address account_,
98:         bool needShareComm_
99:     )
100:         external
101:     {
102:         if (needShareComm_) {
103:             uint amount = _cashIn();
104:             _mint(account_, amount);
105:             shareCommission(account_);
106:         } else {
107:             shareCommission(account_);
108:             uint amount = _cashIn();
109:             _mint(account_, amount);
110:             _sharedA[account_] = balanceOf(account_);
111:             if (_sharedA[account_] > _maxEarningOf[account_]) {
112:                 _updateMaxEarning(account_, _sharedA[account_]);
113:             }
114:         }
115:     }

['130']

130:     function updatePenaltyAddress( // <= FOUND
131:         address penaltyAddress_
132:     )
133:         external
134:         onlyOwner
135:     {
136:         _updatePenaltyAddress(penaltyAddress_);
137:     }

['66']

66:     function updateSponsor( // <= FOUND
67:         address account_,
68:         address sponsor_
69:     )
70:         external
71:         onlyAdmin
72:     {
73:         _updateSponsor(account_, sponsor_);
74:     }

['242']

242:     function updatePower( // <= FOUND
243:         uint voteId_,
244:         bool isForAttacker_
245:     )
246:         external
247:         onlyInVotingTime(voteId_)
248:     {
249:         address voter = msg.sender;
250:         _addVoter(voteId_, voter, isForAttacker_);
251:     }

['45']

45:     function _updateBalance() // <= FOUND
46:         internal
47:     {
48:         _lastestBalance = _token.balanceOf(address(this));
49:     }

['198']

198:     function _updateSponsor( // <= FOUND
199:         address payable poolOwner_,
200:         address staker_,
201:         uint minSPercent_
202:     )
203:         internal
204:     {
205:         if (poolOwner_ == staker_) {
206:             return;
207:         }
208:         IProfile.SProfile memory profile = _profileC.profileOf(poolOwner_);
209:         if (profile.sponsor == staker_) {
210:             return;
211:         }
212:         require(profile.nextSPercent >= minSPercent_, "profile rate changed");
213:         IPoolFactory.SPool memory pool = _poolFactory.getPool(poolOwner_);
214:         IDToken p2UDtoken = IDToken(pool.dToken);
215:         uint timeDiff = block.timestamp - profile.updatedAt;
216:         if (timeDiff > _maxSponsorAfter) {
217:             timeDiff = _maxSponsorAfter;
218:         }
219: 
220:         uint sponsorDTokenBalance = p2UDtoken.balanceOf(profile.sponsor);
221:         uint stakerDTokenBalance = p2UDtoken.balanceOf(staker_);
222:         uint sponsorBonus = sponsorDTokenBalance * (_maxSponsorAdv - 1)
223:             * timeDiff / _maxSponsorAfter;
224:         uint sponsorPower = sponsorDTokenBalance + sponsorBonus;
225:         if (stakerDTokenBalance > sponsorPower || poolOwner_ == profile.sponsor) {
226:             address[] memory pools = new address[](1);
227:             pools[0] = poolOwner_;
228:             earningPulls(poolOwner_, pools, poolOwner_);
229:             _profileC.updateSponsor(poolOwner_, staker_);
230:         }
231:     }

[NonCritical-52] Ensure block.timestamp is only used in long time intervals

Resolution

block.timestamp represents the current block's timestamp and can be influenced, within limits, by miners. For short time intervals, this malleability can be exploited, potentially allowing miners to manipulate contract behavior. For instance, they might fast-forward an expiration or delay an event. When designing smart contracts, if precise time checks are needed for short intervals, alternatives like block numbers can be considered. However, for longer durations where a few seconds of deviation is inconsequential, block.timestamp is generally safe and efficient. Always assess the implications of time manipulations for the specific use-case before utilizing block.timestamp. In practice, if you're using block.timestamp to measure intervals that are a matter of days, weeks, or longer, the potential manipulation by miners becomes less significant. Always prioritize the security and integrity of your smart contract operations when making these decisions.

Num of instances: 2

Findings

Click to show findings

['679']

679:         _voting.createVote(
680:             attacker,
681:             defender_,
682:             aEthValue,
683:             dEthValue_,
684:             voterPercent_,
685:             aQuorum,
686:             block.timestamp,
687:             block.timestamp + freezeDuration_ // <= FOUND
688:         );

['48']

48:         lockData.startedAt = block.timestamp + cliff_; // <= FOUND

[NonCritical-53] Avoid mutating function parameters

Resolution

Function parameters in Solidity are passed by value, meaning they are essentially local copies. Mutating them can lead to confusion and errors because the changes don't persist outside the function. By keeping function parameters immutable, you ensure clarity in code behavior, preventing unintended side-effects. If you need to modify a value based on a parameter, use a local variable inside the function, leaving the original parameter unaltered. By adhering to this practice, you maintain a clear distinction between input data and the internal processing logic, improving code readability and reducing the potential for bugs.

Num of instances: 1

Findings

Click to show findings

['293']

293:     function _stake(
294:         bool isEth_,
295:         address account_,
296:         address payable poolOwner_,
297:         uint duration_,
298:         uint minSPercent_,
299:         uint poolConfigCode_
300:     )
301:         internal
302:     {
303:         if (isEth_) {
304:             if (!_poolFactory.isCreated(account_)) {
305:                 _poolFactory.createPool(account_);
306:                 _ethSharing.initPoolConfig(account_);
307: 
308:                 if (account_ == poolOwner_) {
309:                     
310:                     _profileC.updateSponsor(account_, account_);
311:                 } else {
312:                     
313:                     _profileC.updateSponsor(account_, address(_devTeam));
314:                 }
315:             }
316:             require(_poolFactory.isCreated(poolOwner_), "not activated");
317:         }
318: 
319:         
320:         
321:         
322:         
323:         uint value = isEth_ ? _geth.balanceOf(address(this)) : _dct.balanceOf(address(this));
324:         ILocker locker = isEth_ ? _eLocker : _dLocker;
325:         uint minStakeAmount = isEth_ ? _minStakeETHAmount : _minStakeDCTAmount;
326: 
327:         LLocker.SLock memory oldLockData = locker.getLockData(account_, poolOwner_);
328: 
329:         {
330:         require(value == 0 || value >= minStakeAmount, "amount too small");
331:         require(duration_ == 0 || duration_ >= _minDuration, "duration too small");
332: 
333:         uint rd = LLocker.restDuration(oldLockData);
334:         if (rd + duration_ > _maxDuration) {
335:             duration_ = _maxDuration - rd; // <= FOUND
336:         }
337:         }
338: 
339:         uint powerMinted;
340:         if (isEth_) {
341:             {
342:             _ethSharing.tryResetPool(poolOwner_);
343:             (uint devTeamA, uint poolOwnerA, uint poolUserA, ) =
344:                 _ethSharing.getSharingParts(poolOwner_, value, poolConfigCode_);
345:             _shareDevTeam(devTeamA);
346:             _sharePoolOwner(poolOwnerA, poolOwner_);
347:             _sharePoolUser(poolUserA, poolOwner_);
348:             }
349:             {
350:             uint aLock = _lock(isEth_, poolOwner_, duration_);
351: 
352:             powerMinted = LHelper.calMintStakingPower(
353:                 oldLockData,
354:                 aLock,
355:                 duration_,
356:                 account_ == poolOwner_,
357:                 _selfStakeAdvantage
358:             );
359:             }
360:             IPoolFactory.SPool memory pool = _poolFactory.getPool(poolOwner_);
361:             IDToken p2UDtoken = IDToken(pool.dToken);
362:             bool isFirstStake = p2UDtoken.totalSupply() == 0;
363:             p2UDtoken.mint(account_, powerMinted);
364:             if (isFirstStake) {
365:                 IDistributor(pool.ethDistributor).distribute();
366:             }
367: 
368:             _updateSponsor(poolOwner_, account_, minSPercent_);
369:             emit Stake(isEth_, poolOwner_, account_, value, duration_, powerMinted, isFirstStake);
370:         } else {
371:             {
372:             require(account_ == poolOwner_, "can only stake DCT for yourself");
373:             uint aLock = _lock(isEth_, poolOwner_, duration_);
374: 
375:             powerMinted = LHelper.calMintStakingPower(
376:                 oldLockData,
377:                 aLock,
378:                 duration_,
379:                 false,
380:                 _selfStakeAdvantage
381:             );
382:             }
383:             _dP2PDToken.mint(poolOwner_, powerMinted);
384:             emit Stake(isEth_, poolOwner_, account_, value, duration_, powerMinted, false);

[NonCritical-54] Contracts with only unimplemented functions can be labeled as abstract

Resolution

In Solidity, a contract that's not meant to be deployed on its own but is intended to be inherited by other contracts should be marked abstract. This ensures that developers recognize the contract's incomplete or intended-to-be-extended nature. If a contract has unimplemented functions or is designed with the intention that another contract should extend its functionality, it should be explicitly labeled as abstract. This helps prevent inadvertent deployments and clearly communicates the contract's purpose to other developers. Resolution: Review the contract, and if it's not supposed to function standalone, mark it as abstract to make the intention clear.

Num of instances: 17

Findings

Click to show findings

['8']

8: contract AccessControl is Ownable 

['9']

9: contract AdaptiveDistributor is Cashier, Initializable 

['8']

8: contract Cashier 

['24']

24: contract Controller is UseAccessControl 

['10']

10: contract DToken is PERC20 

['14']

14: contract Distributor is Cashier, Initializable, UseAccessControl 

['12']

12: contract Earning is Cashier, PERC20 

['9']

9: contract EthSharing is UseAccessControl 

['6']

6: contract Initializable 

['13']

13: contract Locker is Cashier, UseAccessControl 

['10']

10: contract ATHBalance 

['16']

16: contract PoolFactory is IPoolFactory, UseAccessControl 

['10']

10: contract PrivateVester is Ownable

['11']

11: contract Profile is IProfile, UseAccessControl 

['38']

38: contract UseAccessControl is Initializable 

['13']

13: contract Vester is Cashier, UseAccessControl 

['17']

17: contract Voting is UseAccessControl, Cashier 

[NonCritical-55] Incorrect bool naming convention

Resolution

It is convention to use isXYZ rather than isNotXYZ for bool values, for example isPaused rather than isNotPaused. This allows for enhanced readability and lower chance of making errors in the creation of conditions.

Num of instances: 1

Findings

Click to show findings

['7']

7:   bool private _isNotInitializable; // <= FOUND

[NonCritical-56] Consider only defining one library/interface/contract per sol file

Resolution

Combining multiple libraries, interfaces, or contracts in a single .sol file can lead to clutter, reduced readability, and versioning issues. Resolution: Adopt the best practice of defining only one library, interface, or contract per Solidity file. This modular approach enhances clarity, simplifies unit testing, and streamlines code review. Furthermore, segregating components makes version management easier, as updates to one component won't necessitate changes to a file housing multiple unrelated components. Structured file management can further assist in avoiding naming collisions and ensure smoother integration into larger systems or DApps.

Num of instances: 3

Findings

Click to show findings

['2']

2: 
3: pragma solidity =0.8.8;
4: 
5: import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6: 
7: interface IATHBalance { // <= FOUND
8:     function athBalance()
9:     external
10:     view
11:     returns(uint);
12: }
13: 
14: interface IPERC20 is IERC20, IATHBalance { // <= FOUND
15:     function initPERC20(
16:         address owner_,
17:         bool inPrivateMode_,
18:         string memory name_,
19:         string memory symbol_
20:     )
21:         external;
22: 
23:     function setInPrivateMode(
24:         bool inPrivateMode_
25:     )
26:         external;
27: 
28:     function mint(
29:         address account_,
30:         uint amount_
31:     )
32:         external;
33: 
34:     function burn(
35:         address account_,
36:         uint amount_
37:     )
38:         external;
39: 
40:     function needAthRecord()
41:         external
42:         view
43:         returns(bool);
44: 
45:     function activeAthRecord()
46:         external;
47: 
48:     function deactiveAthRecord()
49:         external;
50: }

['3']

3: 
5: pragma solidity =0.8.8;
6: pragma abicoder v2;
7: 
8: import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
9: 
10: import "./UseAccessControl.sol";
11: 
12: contract ATHBalance { // <= FOUND
13:     event UpdateAthBalance(
14:         address indexed account_,
15:         uint athBalance
16:     );
17: 
18:     uint private _athBalance;
19: 
20:     function isContract(address _addr) private view returns (bool){
21:     uint32 size;
22:     assembly {
23:         size := extcodesize(_addr)
24:     }
25:     return (size > 0);
26:     }
27: 
28:     function _updateAthBalance(
29:         address account_,
30:         uint balance_
31:     )
32:         internal
33:     {
34:         if (balance_ > _athBalance && !isContract(account_)) {
35:             _athBalance = balance_;
36:             emit UpdateAthBalance(account_, balance_);
37:         }
38:     }
39: 
40:     function athBalance()
41:         external
42:         view
43:         returns(uint)
44:     {
45:         return _athBalance;
46:     }
47: }
48: 
49: contract PERC20 is ERC20, UseAccessControl, ATHBalance { // <= FOUND
50:     event SetInPrivateMode(
51:         bool inPrivateMode
52:     );
53: 
54:     bool private _inPrivateMode;
55:     string private _name;
56:     string private _symbol;
57: 
58:     bool private _needAthRecord;
59: 
60:     function initPERC20(
61:         address accessControl_,
62:         bool inPrivateMode_,
63:         string memory name_,
64:         string memory symbol_
65:     )
66:         public
67:         virtual
68:         initializer
69:     {
70:         initUseAccessControl(accessControl_);
71:         _setInPrivateMode(inPrivateMode_);
72:         _name = name_;
73:         _symbol = symbol_;
74:     }
75: 
76:     function needAthRecord()
77:         external
78:         view
79:         returns(bool)
80:     {
81:         return _needAthRecord;
82:     }
83: 
84:     function activeAthRecord()
85:         external
86:         onlyAdmin
87:     {
88:         _needAthRecord = true;
89:     }
90: 
91:     function deactiveAthRecord()
92:         external
93:         onlyAdmin
94:     {
95:         _needAthRecord = false;
96:     }
97: 
98:     constructor()
99:         ERC20("ignored", "ignored")

['3']

3: 
5: pragma solidity =0.8.8;
6: 
7: import "../modules/interfaces/IWETH.sol";
8: 
9: interface INonfungiblePositionManager { // <= FOUND
10:     struct MintParams {
11:         address token0;
12:         address token1;
13:         uint24 fee;
14:         int24 tickLower;
15:         int24 tickUpper;
16:         uint amount0Desired;
17:         uint amount1Desired;
18:         uint amount0Min;
19:         uint amount1Min;
20:         address recipient;
21:         uint deadline;
22:     }
23: 
24:     function positions(uint256 tokenId)
25:         external
26:         view
27:         returns (
28:             uint96 nonce,
29:             address operator,
30:             address token0,
31:             address token1,
32:             uint24 fee,
33:             int24 tickLower,
34:             int24 tickUpper,
35:             uint128 liquidity,
36:             uint256 feeGrowthInside0LastX128,
37:             uint256 feeGrowthInside1LastX128,
38:             uint128 tokensOwed0,
39:             uint128 tokensOwed1
40:         );
41: 
42:     function burn(uint256 tokenId) external payable;
43: 
44:     function mint(
45:         MintParams calldata params
46:     )
47:         external
48:         payable
49:         returns (uint tokenId, uint128 liquidity, uint amount0, uint amount1);
50: 
51:     struct IncreaseLiquidityParams {
52:         uint tokenId;
53:         uint amount0Desired;
54:         uint amount1Desired;
55:         uint amount0Min;
56:         uint amount1Min;
57:         uint deadline;
58:     }
59: 
60:     function increaseLiquidity(
61:         IncreaseLiquidityParams calldata params
62:     ) external payable returns (uint128 liquidity, uint amount0, uint amount1);
63: 
64:     struct DecreaseLiquidityParams {
65:         uint tokenId;
66:         uint128 liquidity;
67:         uint amount0Min;
68:         uint amount1Min;
69:         uint deadline;
70:     }
71: 
72:     function decreaseLiquidity(
73:         DecreaseLiquidityParams calldata params
74:     ) external payable returns (uint amount0, uint amount1);
75: 
76:     struct CollectParams {
77:         uint tokenId;
78:         address recipient;
79:         uint128 amount0Max;
80:         uint128 amount1Max;
81:     }
82: 
83:     function collect(
84:         CollectParams calldata params
85:     ) external payable returns (uint amount0, uint amount1);
86: }
87: 
88: interface ISwapRouter { // <= FOUND
89:     struct ExactInputSingleParams {
90:         address tokenIn;
91:         address tokenOut;
92:         uint24 fee;
93:         address recipient;
94:         
95:         uint amountIn;
96:         uint amountOutMinimum;
97:         uint160 sqrtPriceLimitX96;
98:     }
99: 
103:     function exactInputSingle(
104:         ExactInputSingleParams calldata params
105:     ) external payable returns (uint amountOut);
106: 
107:     struct ExactInputParams {
108:         bytes path;
109:         address recipient;
110:         uint deadline;
111:         uint amountIn;
112:         uint amountOutMinimum;
113:     }
114: 
118:     function exactInput(
119:         ExactInputParams calldata params
120:     ) external payable returns (uint amountOut);
121: }
122: 
123: interface IUniswapV3Factory { // <= FOUND
124:     function createPool(
125:         address tokenA,
126:         address tokenB,
127:         uint24 fee
128:     ) external returns (address pool);
129: }
130: 
137: library LLido {
138:     ISwapRouter private constant router =
139:         ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
140:     IWETH internal constant weth =
141:         IWETH(0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6);
142: 
145:     IERC20 internal constant wsteth =
146:         IERC20(0x0D47dF42B5d503EcC6A366499B2B97c7D5Ad42eE);
147: 
148:     uint24 private constant POOL_FEE = 100;
149: 
150:     int24 private constant MIN_TICK = -887272;
151:     int24 private constant MAX_TICK = -MIN_TICK;
152:     int24 private constant TICK_SPACING = 60;
153: 
154:     INonfungiblePositionManager private constant nonfungiblePositionManager =
155:         INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
156: 
157:     function createPairForTestnet(
158:         address wstethAddr_
159:     )
160:         internal
161:     {
162:         IUniswapV3Factory factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
163:         factory.createPool(address(weth), wstethAddr_, POOL_FEE);
164:     }
165: 
166:     function add(
167:         uint tokenId_
168:     )
169:         internal
170:         returns(uint128 liquidity)
171:     {
172:         uint amount = weth.balanceOf(address(this));
173:         uint wethA = amount / 2;
174:         buyWsteth(amount - wethA);
175:         uint wstethA = wsteth.balanceOf(address(this));
176:         (liquidity, , ) = increaseLiquidityCurrentRange(tokenId_, wethA, wstethA);
177:     }
178: 
179:     function remove(
180:         uint tokenId_,
181:         uint128 decLiqA_
182:     )
183:         internal
184:     {
185:         decreaseLiquidityCurrentRange(tokenId_, decLiqA_);
186:         collectAllFees(tokenId_);
187:         uint wstethA = wsteth.balanceOf(address(this));

[NonCritical-57] Immutable and constant integer state variables should not be casted

Resolution

The definition of a constant or immutable variable is that they do not change, casting such variables can potentially push more than one 'value' to a constant, for example a uin128 constant can have its original value and that of when it's casted to uint64 (i.e it has some bytes truncated). This can create confusion and inconsistencies within the code which can inadvertently increase the attack surface of the project. It is thus advise to either change the uint byte size in the constant/immutable definition of the variable or introduce a second state variable to cover the differing casts that are expected such as having a uint128 constant and a separate uint64 constant.

Num of instances: 12

Findings

Click to show findings

['208']

208:         weth.approve(address(router), wethA_); // <= FOUND

['244']

244:         wsteth.approve(address(router), wstethA_); // <= FOUND

['161']

161:         factory.createPool(address(weth), wstethAddr_, POOL_FEE); // <= FOUND

['209']

209:         swapExactInputSingleHop(
210:             address(weth), // <= FOUND
211:             address(wsteth),
212:             wethA_
213:         );

['245']

245:         swapExactInputSingleHop(
246:             address(wsteth),
247:             address(weth), // <= FOUND
248:             wstethA_
249:         );

['281']

281:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth); // <= FOUND

['282']

282:         address token1 = token0 == address(weth) ? address(wsteth) : address(weth); // <= FOUND

['283']

283:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_; // <= FOUND

['284']

284:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_; // <= FOUND

['283']

283:         
284:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_; // <= FOUND

['278']

278:         weth.approve(address(nonfungiblePositionManager), wethA_); // <= FOUND

['279']

279:         wsteth.approve(address(nonfungiblePositionManager), wstethA_); // <= FOUND

[NonCritical-58] Empty revert statement

Resolution

Utilizing an empty revert() statement in Solidity might cause potential clarity and debugging issues in smart contract development. While revert() is designed to halt function execution and revert state changes when conditions aren't met, providing a descriptive error message within it enhances debuggability and transparency. Failing to provide specific reasons for reversion makes it difficult to trace the point of failure in contract interactions, especially for external developers and users interacting with the contract. To enhance clarity, developers should always use revert("Descriptive error message") to explain why the execution was halted, ensuring that the cause of reversion is clear, thus aiding in troubleshooting and maintaining transparent contract behavior.

Num of instances: 1

Findings

Click to show findings

['54']

53:         
54:         revert(); // <= FOUND

[NonCritical-59] Long numbers should include underscores to improve readability and prevent typos

Resolution

A large number such as 2000000 is far more readable as 2_000_000, this will help prevent unintended bugs in the code

Num of instances: 4

Findings

Click to show findings

['100']

100:     uint public _selfStakeAdvantage = 15000;  // <= FOUND

['148']

148:     int24 private constant MIN_TICK = -887272; // <= FOUND

['6']

6:     uint constant public DEMI = 10000; // <= FOUND

['34']

34:         uint vestingA = amount_ * vestingPercent_ / 10000; // <= FOUND

[NonCritical-60] Constant block.timestamp range

Resolution

Having a constant integer to dictate a time range can create issues later in the project lifespan where these ranges no longer meet the projects requirements, as such it is advisable to use a uint state variable to hold the increment/decrement value as it can then be changed. Ensure that if a state variable is created, it is capped at a reasonable range.

Num of instances: 1

Findings

Click to show findings

['174']

174:         require(endAt_ < block.timestamp + 365 days, "duration too long"); // <= FOUND

[NonCritical-61] Use 'using' keyword when using specific imports rather than calling the specific import directly

Resolution

In Solidity, the using keyword can streamline the use of library functions for specific types. Instead of calling library functions directly with their full import paths, you can declare a library once with using for a specific type. This approach makes your code more readable and concise. For example, instead of LibraryName.functionName(variable), you would first declare using LibraryName for TypeName; at the contract level. After this, you can call library functions directly on variables of TypeName like variable.functionName(). This method not only enhances code clarity but also promotes cleaner and more organized code, especially when multiple functions from the same library are used frequently.

Num of instances: 78

Findings

Click to show findings

['158']

158:             uint newEP2PBalance = LHelper.calEP2PDBalance( // <= FOUND 'LHelper.'
159:                 _profileC.fsOf(poolOwner_),
160:                 _profileC.boosterOf(poolOwner_),
161:                 p2UDtoken.totalSupply()
162:             );

['178']

178:         _profileC.updateFsOf(account_, LHelper.calFs( // <= FOUND 'LHelper.'
179:             _eEarning.balanceOf(account_) + _voting.defenderEarningFreezedOf(account_),
180:             maxEarning
181:         ));

['352']

352:             powerMinted = LHelper.calMintStakingPower( // <= FOUND 'LHelper.'
353:                 oldLockData,
354:                 aLock,
355:                 duration_,
356:                 account_ == poolOwner_,
357:                 _selfStakeAdvantage
358:             );

['375']

375:             powerMinted = LHelper.calMintStakingPower( // <= FOUND 'LHelper.'
376:                 oldLockData,
377:                 aLock,
378:                 duration_,
379:                 false,
380:                 _selfStakeAdvantage
381:             );

['567']

567:             uint burnedPower = LHelper.calBurnStakingPower(p2UDtoken.balanceOf(account), amount_, oldLockData.amount); // <= FOUND 'LHelper.'

['577']

577:             uint burnedPower = LHelper.calBurnStakingPower(_dP2PDToken.balanceOf(poolOwner_), amount_, oldLockData.amount); // <= FOUND 'LHelper.'

['665']

665:         uint aQuorum = LHelper.calAQuorum( // <= FOUND 'LHelper.'
666:             aEthValue,
667:             dEthValue_,
668:             voterPercent_,
669:             freezeDuration_,
670:             _freezeDurationUnit
671:         );

['405']

405:             LLido.allToWsteth(minWstethA_); // <= FOUND 'LLido.'

['510']

510:             LLido.sellWsteth(amountForPuller); // <= FOUND 'LLido.'

['274']

274:             LLido.wethToEth(); // <= FOUND 'LLido.'

['571']

571:             LLido.allToEth(minEthA_); // <= FOUND 'LLido.'

['633']

633:                 LLido.allToEth(minEthA_); // <= FOUND 'LLido.'

['728']

728:         LLido.allToEth(0); // <= FOUND 'LLido.'

['273']

273:             
274:             LLido.sellWsteth(toWinnerVal); // <= FOUND 'LLido.'

['327']

327:         LLocker.SLock memory oldLockData = locker.getLockData(account_, poolOwner_); // <= FOUND 'LLocker.'

['34']

34:         uint rd = LLocker.restDuration(oldLockData); // <= FOUND 'LLocker.'

['557']

557:         LLocker.SLock memory oldLockData = locker.getLockData(account, poolOwner_); // <= FOUND 'LLocker.'

['602']

602:             LLocker.SLock memory oldLockData = _eLocker.getLockData(account, poolOwner_); // <= FOUND 'LLocker.'

['603']

603:             uint realDuration = duration_ + LLocker.restDuration(oldLockData); // <= FOUND 'LLocker.'

['46']

46:     function getLockDataById(
47:         bytes32 lockId_
48:     )
49:         external
50:         view
51:         returns(LLocker.SLock memory); // <= FOUND 'LLocker.'

['53']

53:     function getLockData(
54:         address account_,
55:         address poolOwner_
56:     )
57:         external
58:         view
59:         returns(LLocker.SLock memory); // <= FOUND 'LLocker.'

['49']

49:     function getLockData(
50:         address account_
51:     )
52:         external
53:         view
54:         returns(LLocker.SLock memory); // <= FOUND 'LLocker.'

['23']

23:     function calMintStakingPower(
24:         LLocker.SLock memory oldLockData, // <= FOUND 'LLocker.'
25:         uint lockAmount_,
26:         uint lockTime_,
27:         bool isSelfStake_,
28:         uint selfStakeAdvantage_
29:     )
30:         internal
31:         view
32:         returns(uint)
33:     {

['16']

16:     event UpdateLockData(
17:         address indexed account,
18:         address indexed poolOwner,
19:         LLocker.SLock lockData // <= FOUND 'LLocker.'
20:     );

['37']

37:     mapping(bytes32 => LLocker.SLock) private _lockData; // <= FOUND 'LLocker.'

['67']

67:         bytes32 lockId = LLocker.getLockId(account_, poolOwner_); // <= FOUND 'LLocker.'

['69']

69:         LLocker.prolong(_lockData[lockId], amount, duration_); // <= FOUND 'LLocker.'

['97']

97:         LLocker.SLock storage lockData = _lockData[lockId]; // <= FOUND 'LLocker.'

['101']

101:             require(LLocker.isUnlocked(lockData, fs, isPoolOwner), "not unlocked"); // <= FOUND 'LLocker.'

['103']

103:         uint duration = LLocker.calDuration(lockData, fs, isPoolOwner); // <= FOUND 'LLocker.'

['155']

155:         return LLocker.getLockId(account_, poolOwner_); // <= FOUND 'LLocker.'

['158']

158:     function getLockDataById(
159:         bytes32 lockId_
160:     )
161:         external
162:         view
163:         returns(LLocker.SLock memory) // <= FOUND 'LLocker.'
164:     {

['168']

168:     function getLockData(
169:         address account_,
170:         address poolOwner_
171:     )
172:         external
173:         view
174:         returns(LLocker.SLock memory) // <= FOUND 'LLocker.'
175:     {

['16']

16:     event UpdateLockData(
17:         address indexed account,
18:         LLocker.SLock lockData // <= FOUND 'LLocker.'
19:     );

['21']

21:     mapping(address => LLocker.SLock) private _lockData; // <= FOUND 'LLocker.'

['45']

45:         LLocker.SLock storage lockData = _lockData[account_]; // <= FOUND 'LLocker.'

['62']

62:         LLocker.SLock storage lockData; // <= FOUND 'LLocker.'

['116']

116:         LLocker.SLock memory lockData = _lockData[account_]; // <= FOUND 'LLocker.'

['117']

117:         restDuration = LLocker.restDuration(lockData); // <= FOUND 'LLocker.'

['135']

135:     function getLockData(
136:         address account_
137:     )
138:         external
139:         view
140:         returns(LLocker.SLock memory) // <= FOUND 'LLocker.'
141:     {

['439']

439:         uint taxA = LPercentage.getPercentA(amount_, _dctTaxPercent); // <= FOUND 'LPercentage.'

['508']

508:             uint256 amountForPuller = _geth.balanceOf(address(this)) * _bountyPullEarningPercent / LPercentage.DEMI; // <= FOUND 'LPercentage.'

['663']

663:         require(aEthValue <= dEthValue_ && aEthValue * LPercentage.DEMI / dEthValue_ >= _minAttackerFundRate, "aEthValue invalid"); // <= FOUND 'LPercentage.'

['67']

67:         return ownerPercent_ * LPercentage.DEMI + userPercent_; // <= FOUND 'LPercentage.'

['88']

88:        LPercentage.validatePercent(ownerPercent_ + userPercent_); // <= FOUND 'LPercentage.'

['183']

183:         return LPercentage.DEMI - poolConfig.ownerPercent - poolConfig.userPercent; // <= FOUND 'LPercentage.'

['193']

193:         uint amount = LPercentage.getPercentA(value_, devTeamPercent); // <= FOUND 'LPercentage.'

['218']

218:         uint amount = LPercentage.getPercentA(sysExcPart, poolConfig.ownerPercent); // <= FOUND 'LPercentage.'

['232']

232:         uint amount = LPercentage.getPercentA(sysExcPart, poolConfig.userPercent); // <= FOUND 'LPercentage.'

['20']

20:        return fs_ * booster_ * totalP2UBalance_ / LPercentage.DEMIE2; // <= FOUND 'LPercentage.'

['41']

41:         uint rs = (oldALock * calMultiplierForOldAmount(dLockForOldA) + lockAmount_ * calMultiplier(dLockForStakeA)) / LPercentage.DEMI; // <= FOUND 'LPercentage.'

['43']

43:             rs = rs * selfStakeAdvantage_ / LPercentage.DEMI; // <= FOUND 'LPercentage.'

['33']

33:             return LPercentage.DEMI; // <= FOUND 'LPercentage.'

['76']

76:         return earningBalance_ * LPercentage.DEMI / max; // <= FOUND 'LPercentage.'

['86']

86:         uint x = lockTime_ * LPercentage.DEMI / 30 days; // <= FOUND 'LPercentage.'

['87']

87:         uint rs = (1300 * x / LPercentage.DEMI); // <= FOUND 'LPercentage.'

['99']

99:         uint rs = (1300 * x / LPercentage.DEMI) + 8800; // <= FOUND 'LPercentage.'

['114']

114:         uint tmp = LPercentage.DEMI - voterPercent_; // <= FOUND 'LPercentage.'

['115']

115:         uint leverage = LPercentage.DEMIE2 * // <= FOUND 'LPercentage.'
116:             dEthValue_ * freezeDuration_ / aEthValue_ / freezeDurationUnit_ / tmp;

['117']

117:         if (leverage < LPercentage.DEMI) { // <= FOUND 'LPercentage.'

['118']

118:             leverage = LPercentage.DEMI; // <= FOUND 'LPercentage.'

['120']

120:         return LPercentage.DEMI * leverage / (leverage + LPercentage.DEMI); // <= FOUND 'LPercentage.'

['77']

77:         uint mFactor = isPoolOwner_ ? 2 * LPercentage.DEMI - fs_ : fs_; // <= FOUND 'LPercentage.'

['78']

78:         uint duration = lockData_.duration * mFactor / LPercentage.DEMI; // <= FOUND 'LPercentage.'

['19']

19:         return LPercentage.DEMI - value_; // <= FOUND 'LPercentage.'

['35']

35:         return LPercentage.DEMI + LPercentage.DEMI * (maxBooster_ - 1) * boostVotePower_ / max; // <= FOUND 'LPercentage.'

['82']

82:         LPercentage.validatePercent(sPercent_); // <= FOUND 'LPercentage.'

['142']

142:         profileData.bonusBooster = booster_ - LPercentage.DEMI; // <= FOUND 'LPercentage.'

['167']

167:             sAmount = LPercentage.getPercentA(amount_, profileData.sPercent); // <= FOUND 'LPercentage.'

['192']

192:         return LPercentage.DEMI + profileData.bonusBooster; // <= FOUND 'LPercentage.'

['163']

163:         LPercentage.validatePercent(voterPercent_); // <= FOUND 'LPercentage.'

['164']

164:         LPercentage.validatePercent(aQuorum_); // <= FOUND 'LPercentage.'

['264']

264:         uint reqPower = LPercentage.getPercentA(totalPower, vote.aQuorum); // <= FOUND 'LPercentage.'

['268']

268:             vote.winVal = LPercentage.getPercentA(vote.dEthValue, vote.voterPercent); // <= FOUND 'LPercentage.'

['278']

278:             vote.winVal = LPercentage.getPercentA(vote.aEthValue, vote.voterPercent); // <= FOUND 'LPercentage.'

['193']

193:         uint newBooster = LProfile.calBooster(boostVotePower, maxBoostVotePower, _maxBooster); // <= FOUND 'LProfile.'

['130']

130:         profileData.ifs = LProfile.invertOf(fs_); // <= FOUND 'LProfile.'

['181']

181:         return LProfile.invertOf(profileData.ifs); // <= FOUND 'LProfile.'

[NonCritical-62] Avoid declaring variables with the names of defined functions within the project

Resolution

Having such variables can create confusion in both developers and in users of the project. Consider renaming these variables to improve code clarity.

Num of instances: 4

Findings

Click to show findings

['224']

224:         uint restDuration = voteDuration - pastTime; // <= FOUND

['40']

35:     function currentLockData(
36:         address account_
37:     )
38:         external
39:         view
40:         returns(uint restA, uint restDuration); // <= FOUND

['83']

83:         (uint restA, uint restDuration) = currentLockData(account_); // <= FOUND

['128']

109:     
123:     function currentLockData(
124:         address account_
125:     )
126:         public
127:         view
128:         returns(uint restA, uint restDuration) // <= FOUND
129:     {

[NonCritical-63] Constructors should emit an event

Resolution

Emitting an event in a constructor of a smart contract provides transparency and traceability in blockchain applications. This event logs the contract’s creation, aiding in monitoring and verifying contract deployment. Although constructors are executed only once, the emitted event ensures the contract's initialization is recorded on the blockchain.

Num of instances: 1

Findings

Click to show findings

['8']

8:     constructor() { // <= FOUND
9:         _transferOwnership(msg.sender);
10:     }

[NonCritical-64] Consider using AccessControlDefaultAdminRules rather than AccessControl

Resolution

Using AccessControlDefaultAdminRules over AccessControl in Solidity contracts provides a predefined set of rules for administrative privileges, simplifying the management of roles and permissions. It offers a structured approach to defining who can grant or revoke roles, enhancing security and governance within contracts by standardizing access controls and reducing the risk of misconfiguration.

Num of instances: 2

Findings

Click to show findings

['7']

7: contract GlobalAccessControl is AccessControl  // <= FOUND

['7']

7: contract GlobalAccessControl is AccessControl  // <= FOUND

[NonCritical-65] Avoid using 'owner' or '_owner' as a parameter name

Resolution

Using 'owner' or '_owner' as a parameter name in functions, especially in contracts inheriting from or interacting with OpenZeppelin's Ownable contract, can lead to confusion and potential bugs. These contracts often have a state variable named owner, managed by the Ownable pattern for access control. Using the same name for function parameters may obscure the contract's owner state variable, complicating code readability and maintenance. Prefer alternative descriptive names for parameters to maintain clarity and prevent conflicts with established ownership patterns.

Num of instances: 4

Findings

Click to show findings

['66']

65:     function _createPool(
66:         address owner_ // <= FOUND
67:     )
68:         internal
69:     

['105']

104:     function createPool(
105:         address owner_ // <= FOUND
106:     )
107:         external
108:         onlyAdmin
109:     

['115']

114:     function isCreated(
115:         address owner_ // <= FOUND
116:     )
117:         external
118:         view
119:         returns(bool)
120:     

['125']

124:     function getPool(
125:         address owner_ // <= FOUND
126:     )
127:         external
128:         view
129:         returns(SPool memory)
130:     

[NonCritical-66] Memory-safe annotation missing

Resolution

Tagging assembly blocks as "memory safe" in Solidity helps maintainers and auditors quickly identify areas of the code less likely to introduce vulnerabilities due to improper memory access. This practice promotes safer contract development by clearly communicating assumptions about memory handling, aiding in the review process, and reducing the risk of introducing memory-related bugs. It's a part of best coding practices, enhancing code clarity and security, especially in complex, low-level operations where Solidity's safety checks are bypassed.

Num of instances: 1

Findings

Click to show findings

['136']

136:     assembly {
137:         size := extcodesize(_addr)
138:     }

[Gas-1] State variables used within a function more than once should be cached to save gas

Resolution

Cache such variables and perform operations on them, if operations include modifications to the state variable(s) then remember to equate the state variable to it's cached counterpart at the end

Num of instances: 5

Findings

Click to show findings

['147']

147:     function _reCalEP2PDBalance( // <= FOUND 'function _reCalEP2PDBalance'
148:         address poolOwner_
149:     )
150:         internal
151:     {
152:         if (_poolFactory.isCreated(poolOwner_)) {
153:             IPoolFactory.SPool memory pool = _poolFactory.getPool(poolOwner_);
154:             IDToken p2UDtoken = IDToken(pool.dToken);
155: 
156:             uint oldEP2PBalance = _eP2PDToken.balanceOf(pool.dctDistributor);
157: 
158:             uint newEP2PBalance = LHelper.calEP2PDBalance(
159:                 _profileC.fsOf(poolOwner_), // <= FOUND '_profileC'
160:                 _profileC.boosterOf(poolOwner_), // <= FOUND '_profileC'
161:                 p2UDtoken.totalSupply()
162:             );
163:             if (newEP2PBalance > oldEP2PBalance) {
164:                 _eP2PDToken.mint(pool.dctDistributor, newEP2PBalance - oldEP2PBalance);
165:             } else if (newEP2PBalance < oldEP2PBalance) {
166:                 _eP2PDToken.burn(pool.dctDistributor, oldEP2PBalance - newEP2PBalance);
167:             }
168:         }
169:     }

['267']

267:     function _lock( // <= FOUND 'function _lock'
268:         bool isEth_,
269:         address payable poolOwner_,
270:         uint duration_
271:     )
272:         internal
273:         returns (uint)
274:     {
275: 
276:         if (isEth_) {
277:             uint value = _geth.balanceOf(address(this));
278:             _eLocker.clean(); // <= FOUND '_eLocker'
279:             _geth.transfer(address(_eLocker), value); // <= FOUND '_eLocker'
280:             _eLocker.lock(msg.sender, poolOwner_, duration_); // <= FOUND '_eLocker'
281: 
282:             return value;
283:         } else {
284:             uint value = _dct.balanceOf(address(this));
285:             _dLocker.clean();
286:             _dct.transfer(address(_dLocker), value);
287:             _dLocker.lock(msg.sender, poolOwner_, duration_);
288: 
289:             return value;
290:         }
291:     }

['27']

27:     function initDCT( // <= FOUND 'function initDCT'
28:         address accessControl_,
29:         address rewardPool_,
30:         address premineAddress_,
31:         uint256 premineAmount_,
32:         address cleanTo_
33:     )
34:         external
35:         initializer
36:     {
37:         initUseAccessControl(accessControl_);
38: 
39:         _rewardPool = rewardPool_;
40:         _vester = new Vester(); // <= FOUND '_vester'
41:         _vester.initVester(accessControl_, address(this), cleanTo_); // <= FOUND '_vester'
42: 
43:         _mint(premineAddress_, premineAmount_);
44:     }

['25']

25:     function _sendTo( // <= FOUND 'function _sendTo'
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     {
34:         uint vestingA = amount_ * vestingPercent_ / 10000;
35:         uint directA = amount_ - vestingA;
36:         _token.transfer(invester_, directA); // <= FOUND '_token'
37:         _token.transfer(address(_vester), vestingA); // <= FOUND '_vester'
38:         _vester.lock(invester_, vestingDur_, cliff_); // <= FOUND '_vester'
39:     }

['65']

65:     function _createPool( // <= FOUND 'function _createPool'
66:         address owner_
67:     )
68:         internal
69:     {
70:         require(!_isCreated[owner_], "already created");
71:         _isCreated[owner_] = true;
72: 
73:         uint salt = uint256(uint160(owner_));
74:         SPool storage pool = _pools[owner_];
75:         pool.dToken = _deploy(_dTokenBytecode, salt);
76:         pool.ethDistributor = _deploy(_distributorBytecode, salt);
77:         pool.dctDistributor = _deploy(_distributorBytecode, salt + 1);
78: 
79:         address[] memory poolDistributorAddrs = new address[](2);
80:         poolDistributorAddrs[0] = pool.ethDistributor;
81:         poolDistributorAddrs[1] = pool.dctDistributor;
82:         IDToken(pool.dToken).initDToken(
83:             address(_accessControl), // <= FOUND '_accessControl'
84:             _inPrivateMode, // <= FOUND '_inPrivateMode'
85:             DIV_TOKEN_NAME,
86:             DIV_TOKEN_SYMBOL,
87:             poolDistributorAddrs
88:         );
89: 
90:         IDistributor(pool.ethDistributor).initDistributor(
91:             address(_accessControl), // <= FOUND '_accessControl'
92:             pool.dToken,
93:             _geth
94:         );
95: 
96:         IDistributor(pool.dctDistributor).initDistributor(
97:             address(_accessControl), // <= FOUND '_accessControl'
98:             pool.dToken,
99:             _dct
100:         );
101:         emit CreatePool(owner_, pool);
102:     }

[Gas-2] The result of a function call should be cached rather than re-calling the function

Resolution

External calls in Solidity are costly in terms of gas usage. This can significantly impact contract efficiency and cost. Functions that make repetitive calls to fetch the same data from other contracts can cause unnecessary gas expenditure. To optimize this, it's advisable to store the returned value of these function calls in a state variable, essentially caching the data. This data can be updated at regular intervals or under specific conditions instead of fetching it from the external contract on every invocation. Be sure to analyze the frequency of data change in the external contract to balance data freshness with gas efficiency when implementing caching.

Num of instances: 1

Findings

Click to show findings

['51']

51:     function distribute()
52:         public
53:     {
54:         if (regSupply == 0) return;
55:         uint addedAmount = _cashIn(); // <= FOUND
56:         if (addedAmount > 0) {
57:             rpt += _cashIn() * MFACTOR / regSupply; // <= FOUND
58: 
59:             emit Distribute(addedAmount, regSupply);
60:         }
61:     }

[Gas-3] Multiple accesses of the same mapping/array key/index should be cached

Resolution

Caching repeated accesses to the same mapping or array key/index in smart contracts can lead to significant gas savings. In Solidity, each read operation from storage (like accessing a value in a mapping or array using a key or index) costs gas. By storing the accessed value in a local variable and reusing it within the function, you avoid multiple expensive storage read operations. This practice is particularly beneficial in loops or functions with multiple reads of the same data. Implementing this caching approach enhances efficiency and reduces transaction costs, which is crucial for optimizing smart contract performance and user experience on the blockchain.

Num of instances: 9

Findings

Click to show findings

['73']

73:     function shareCommission(
74:         address account_
75:     )
76:         public
77:     {
78:         uint amount = balanceOf(account_) - _sharedA[account_]; // <= FOUND
79:         if (amount == 0) {
80:             return;
81:         }
82: 
83:         address sponsor;
84:         uint sAmount;
85:         (sponsor, sAmount) =  _profileC.getSponsorPart(account_, amount);
86:         if (sAmount > 0) {
87:             _transfer(account_, sponsor, sAmount);
88:             emit ShareCommission(account_, sponsor, sAmount);
89:         }
90:         _sharedA[account_] = balanceOf(account_); // <= FOUND
91:         if (_sharedA[account_] > _maxEarningOf[account_]) { // <= FOUND
92:             _updateMaxEarning(account_, _sharedA[account_]); // <= FOUND
93:         }
94:     }

['96']

96:     function update(
97:         address account_,
98:         bool needShareComm_
99:     )
100:         external
101:     {
102:         if (needShareComm_) {
103:             uint amount = _cashIn();
104:             _mint(account_, amount);
105:             shareCommission(account_);
106:         } else {
107:             shareCommission(account_);
108:             uint amount = _cashIn();
109:             _mint(account_, amount);
110:             _sharedA[account_] = balanceOf(account_); // <= FOUND
111:             if (_sharedA[account_] > _maxEarningOf[account_]) { // <= FOUND
112:                 _updateMaxEarning(account_, _sharedA[account_]); // <= FOUND
113:             }
114:         }
115:     }

['144']

144:     function earningOf(
145:         address account_
146:     )
147:         external
148:         view
149:         returns(uint)
150:     {
151:         uint amount = balanceOf(account_) - _sharedA[account_]; // <= FOUND
152:         if (amount == 0) {
153:             return _sharedA[account_]; // <= FOUND
154:         }
155: 
156:         address sponsor;
157:         uint sAmount;
158:         (sponsor, sAmount) =  _profileC.getSponsorPart(account_, amount);
159:         return balanceOf(account_) - sAmount;
160:     }

['59']

59:     function lock(
60:         address account_,
61:         address poolOwner_,
62:         uint duration_
63:     )
64:         external
65:         onlyAdmin
66:     {
67:         bytes32 lockId = LLocker.getLockId(account_, poolOwner_);
68:         uint amount = _cashIn();
69:         LLocker.prolong(_lockData[lockId], amount, duration_); // <= FOUND
70:         emit UpdateLockData(account_, poolOwner_, _lockData[lockId]); // <= FOUND
71:     }

['87']

87:     function _withdraw(
88:         address account_,
89:         address poolOwner_,
90:         address dest_,
91:         uint amount_,
92:         bool isForced_
93:     )
94:         internal
95:     {
96:         bytes32 lockId = LLocker.getLockId(account_, poolOwner_);
97:         LLocker.SLock storage lockData = _lockData[lockId]; // <= FOUND
98:         bool isPoolOwner = account_ == poolOwner_;
99:         uint fs = _profileC.fsOf(poolOwner_);
100:         if (!isForced_) {
101:             require(LLocker.isUnlocked(lockData, fs, isPoolOwner), "not unlocked");
102:         }
103:         uint duration = LLocker.calDuration(lockData, fs, isPoolOwner);
104:         uint pastTime = block.timestamp - lockData.startedAt;
105:         if (pastTime > duration) {
106:             pastTime = duration;
107:         }
108: 
109:         lockData.amount -= amount_;
110:         uint total = amount_;
111:         uint receivedA = total * pastTime / duration;
112:         _cashOut(dest_, receivedA);
113:         if (total != receivedA) {
114:             _cashOut(_penaltyAddress, total - receivedA);
115:         }
116: 
117:         emit Withdraw(account_, poolOwner_, dest_, amount_);
118:         emit UpdateLockData(account_, poolOwner_, _lockData[lockId]); // <= FOUND
119:     }

['35']

35:     function lock(
36:         address account_,
37:         uint duration_,
38:         uint cliff_
39:     )
40:         external
41:         onlyAdmin
42:     {
43:         uint amount = _cashIn();
44:         unlock(account_);
45:         LLocker.SLock storage lockData = _lockData[account_]; // <= FOUND
46:         lockData.amount += amount;
47:         lockData.duration = duration_;
48:         lockData.startedAt = block.timestamp + cliff_;
49:         emit UpdateLockData(account_, _lockData[account_]); // <= FOUND
50:     }

['75']

75:     function unlock(
76:         address account_
77:     )
78:         public
79:     {
80:         LLocker.SLock storage lockData = _lockData[account_]; // <= FOUND
81:         if (lockData.amount == 0 || lockData.startedAt > block.timestamp) return;
82:         uint airdrop = _cashIn();
83:         (uint restA, uint restDuration) = currentLockData(account_);
84:         uint toUnlockA = lockData.amount - restA;
85:         lockData.startedAt = block.timestamp;
86:         lockData.amount = restA;
87:         lockData.duration = restDuration;
88:         uint toTransferA = toUnlockA + airdrop;
89:         if (toTransferA > 0) {
90:             _cashOut(account_, toUnlockA + airdrop);
91:         }
92:         emit UpdateLockData(account_, _lockData[account_]); // <= FOUND
93:     }

['191']

191:     function _removeVoter(
192:         uint voteId_,
193:         address voter_
194:     )
195:         internal
196:     {
197:         SVote storage vote = _votes[voteId_]; // <= FOUND
198:         uint power = vote.powerOf[voter_]; // <= FOUND
199:         if (power > 0) {
200:             if (vote.isForAttacker[voter_]) { // <= FOUND
201:                 vote.attackerPower -= power;
202:             } else {
203:                 vote.defenderPower -= power;
204:             }
205:             vote.powerOf[voter_] = 0; // <= FOUND
206:             emit RemoveVoter(voteId_, voter_, vote.isForAttacker[voter_], power); // <= FOUND
207:         }
208:     }

['344']

344:     function getVote(
345:         uint voteId_
346:     )
347:         external
348:         view
349:         returns(IVoting.SVoteBasicInfo memory)
350:     {
351:         IVoting.SVoteBasicInfo memory basicInfo;
352: 
353:         basicInfo.attacker = _votes[voteId_].attacker; // <= FOUND
354:         basicInfo.defender = _votes[voteId_].defender; // <= FOUND
355:         basicInfo.aEthValue = _votes[voteId_].aEthValue; // <= FOUND
356:         basicInfo.dEthValue = _votes[voteId_].dEthValue; // <= FOUND
357:         basicInfo.voterPercent = _votes[voteId_].voterPercent; // <= FOUND
358:         basicInfo.aQuorum = _votes[voteId_].aQuorum; // <= FOUND
359:         basicInfo.startedAt = _votes[voteId_].startedAt; // <= FOUND
360:         basicInfo.endAt = _votes[voteId_].endAt; // <= FOUND
361:         basicInfo.attackerPower = _votes[voteId_].attackerPower; // <= FOUND
362:         basicInfo.defenderPower = _votes[voteId_].defenderPower; // <= FOUND
363:         basicInfo.totalClaimed = _votes[voteId_].totalClaimed; // <= FOUND
364:         basicInfo.isFinalized = _votes[voteId_].isFinalized; // <= FOUND
365:         basicInfo.isAttackerWon = _votes[voteId_].isAttackerWon; // <= FOUND
366:         basicInfo.winVal = _votes[voteId_].winVal; // <= FOUND
367:         basicInfo.winnerPower = _votes[voteId_].winnerPower; // <= FOUND
368:         basicInfo.isClosed = _votes[voteId_].isClosed; // <= FOUND
369: 
370:         return basicInfo;
371:     }

[Gas-4] Shortcircuit rules can be be used to optimize some gas usage

Resolution

Reading state variables takes alot of gas to do, so if you have a conditional statement regarding a state varaible alongide others which are not related to state vars, it is advisable to have conditions which do not involve state vars be declared first in the overall conditional so unnecessary state variable lookups can be avoided.

Num of instances: 1

Findings

Click to show findings

['32']

32:         if (balance_ > _athBalance && !isContract(account_)) { // <= FOUND

[Gas-5] Use assembly to write address/contract storage values

Num of instances: 71

Findings

Click to show findings

['82']

82:         _isPaused = true; // <= FOUND

['89']

89:         _isPaused = false; // <= FOUND

['47']

47:         _dToken = IERC20(dToken_); // <= FOUND

['86']

86:                 rpt = 0; // <= FOUND

['31']

31:         _token = IERC20(token_); // <= FOUND

['40']

40:         _cleanTo = cleanTo_; // <= FOUND

['114']

114:         _geth = IERC20(contracts_[0]); // <= FOUND

['115']

115:         _dct = IDCT(contracts_[1]); // <= FOUND

['118']

118:         _devTeam = IDistributor(contracts_[3]); // <= FOUND

['119']

119:         _poolFactory = IPoolFactory(contracts_[4]); // <= FOUND

['120']

120:         _profileC = IProfile(contracts_[5]); // <= FOUND

['122']

122:         _eLocker = ILocker(contracts_[6]); // <= FOUND

['123']

123:         _eP2PDToken = IDToken(contracts_[7]); // <= FOUND

['125']

125:         _eEarning = IEarning(contracts_[8]); // <= FOUND

['127']

127:         _dLocker = ILocker(contracts_[9]); // <= FOUND

['128']

128:         _dP2PDToken = IDToken(contracts_[10]); // <= FOUND

['129']

129:         _dP2PDistributor = IDistributor(contracts_[11]); // <= FOUND

['130']

130:         _dEarning = IEarning(contracts_[12]); // <= FOUND

['132']

132:         _voting = IVoting(contracts_[13]); // <= FOUND

['135']

135:         _eDP2PDistributor = IDistributor(contracts_[14]); // <= FOUND

['137']

137:         _dDP2PDistributor = IDistributor(contracts_[15]); // <= FOUND

['141']

141:         _ethSharing = IEthSharing(contracts_[16]); // <= FOUND

['741']

741:         _bountyPullEarningPercent = values_[0]; // <= FOUND

['743']

743:         _maxBooster = values_[1]; // <= FOUND

['745']

745:         _maxSponsorAdv = values_[2]; // <= FOUND

['746']

746:         _maxSponsorAfter = values_[3]; // <= FOUND

['748']

748:         _attackFee = values_[4]; // <= FOUND

['749']

749:         _maxVoterPercent = values_[5]; // <= FOUND

['750']

750:         _minAttackerFundRate = values_[6]; // <= FOUND

['751']

751:         _freezeDurationUnit = values_[7]; // <= FOUND

['752']

752:         _selfStakeAdvantage = values_[8]; // <= FOUND

['755']

755:         _isPausedAttack = values_[10]; // <= FOUND

['759']

759:         _dctTaxPercent = values_[12]; // <= FOUND

['761']

761:         _minFreezeDuration = values_[13]; // <= FOUND

['762']

762:         _maxFreezeDuration = values_[14]; // <= FOUND

['764']

764:         _minStakeETHAmount = values_[15]; // <= FOUND

['765']

765:         _minStakeDCTAmount = values_[16]; // <= FOUND

['767']

767:         _minDefenderFund = values_[17]; // <= FOUND

['39']

39:         _rewardPool = rewardPool_; // <= FOUND

['40']

40:         _vester = new Vester(); // <= FOUND

['52']

52:         _lastHalved = block.timestamp; // <= FOUND

['104']

104:             _lastHalved = block.timestamp; // <= FOUND

['103']

103:             _tps = _tps / 2; // <= FOUND

['25']

25:         _totalDistributors = distributorAddrs_.length; // <= FOUND

['43']

43:         _dToken = IDToken(dToken_); // <= FOUND

['54']

54:         _profileC = IProfile(profileCAddr_); // <= FOUND

['107']

107:         devTeamPercent = devTeamPercent_; // <= FOUND

['108']

108:         defaultOwnerPercent = defaultOwnerPercent_; // <= FOUND

['109']

109:         defaultUserPercent = defaultUserPercent_; // <= FOUND

['110']

110:         defaultCode = getCode(defaultOwnerPercent_, defaultUserPercent_); // <= FOUND

['112']

112:         inDefaultOnlyMode = inDefaultOnlyMode_; // <= FOUND

['15']

15:     _isNotInitializable = true; // <= FOUND

['126']

126:         _penaltyAddress = penaltyAddress_; // <= FOUND

['33']

33:             _athBalance = balance_; // <= FOUND

['70']

70:         _name = name_; // <= FOUND

['71']

71:         _symbol = symbol_; // <= FOUND

['86']

86:         _needAthRecord = true; // <= FOUND

['93']

93:         _needAthRecord = false; // <= FOUND

['132']

132:         _inPrivateMode = inPrivateMode_; // <= FOUND

['44']

44:         _geth = geth_; // <= FOUND

['45']

45:         _dct = dct_; // <= FOUND

['46']

46:         _dTokenBytecode = type(DToken).creationCode; // <= FOUND

['47']

47:         _distributorBytecode = type(Distributor).creationCode; // <= FOUND

['21']

21:         _vester = vester_; // <= FOUND

['22']

22:         _token = token_; // <= FOUND

['109']

109:         _defaultSPercent = sPercent_; // <= FOUND

['119']

119:         _minSPercent = sPercent_; // <= FOUND

['79']

79:         _accessControl = IAccessControl(accessControl_); // <= FOUND

['130']

130:         _votingToken = IERC20(votingTokenAddr_); // <= FOUND

['131']

131:         _geth = IERC20(gethAddr_); // <= FOUND

['133']

133:         _eEarning = IEarning(eEarningAddr_); // <= FOUND

[Gas-6] x + y is more efficient than using += for state variables (likewise for -=)

Resolution

In instances found where either += or -= are used against state variables use x = x + y instead

Num of instances: 3

Findings

Click to show findings

['57']

51:     function distribute()
52:         public
53:     {
54:         if (regSupply == 0) return;
55:         uint addedAmount = _cashIn();
56:         if (addedAmount > 0) {
57:             rpt += _cashIn() * MFACTOR / regSupply; // <= FOUND
58: 
59:             emit Distribute(addedAmount, regSupply);
60:         }
61:     }

['89']

69:     function claimFor(
70:         address holder_
71:     )
72:         public
73:     {
74:         distribute();
75: 
76:         uint bal = _dToken.balanceOf(holder_);
77:         SHolder storage holder = holders[holder_];
78:         uint deltaRpt = rpt - holder.lastRpt;
79:         uint reward;
80:         if (holder.regBal > bal) {
81:             reward = deltaRpt * bal / MFACTOR;
82:             uint unregBal = holder.regBal - bal;
83:             uint removedReward = deltaRpt * unregBal / MFACTOR;
84:             regSupply -= unregBal;
85:             if (regSupply == 0) {
86:                 rpt = 0;
87:                 
88:             } else {
89:                 rpt += removedReward * MFACTOR / regSupply; // <= FOUND
90:             }
91:         } else {
92:             reward = deltaRpt * holder.regBal / MFACTOR;
93:             regSupply += bal - holder.regBal;
94:         }
95: 
96:         if (reward > 0) {
97:             _cashOut(holder_, reward);
98:             emit Claim(holder_, reward);
99:         }
100: 
101:         if (holder.regBal != bal) {
102:             emit UpdateRegBal(holder_, bal);
103:         }
104: 
105:         holder.regBal = bal;
106:         holder.lastClaimedAt = block.timestamp;
107:         holder.lastRpt = rpt;
108:     }

['62']

56:     function _distribute()
57:         internal
58:     {
59:         uint totalSupply = _dToken.totalSupply();
60:         if (totalSupply != 0) {
61:             uint amount = _cashIn();
62:             _ptr += amount * MFACTOR / totalSupply; // <= FOUND
63:         }
64:     }

[Gas-7] Public functions not used internally can be marked as external to save gas

Resolution

Public functions that aren't used internally in Solidity contracts should be made external to optimize gas usage and improve contract efficiency. External functions can only be called from outside the contract, and their arguments are directly read from the calldata, which is more gas-efficient than loading them into memory, as is the case for public functions. By using external visibility, developers can reduce gas consumption for external calls and ensure that the contract operates more cost-effectively for users. Moreover, setting the appropriate visibility level for functions also enhances code readability and maintainability, promoting a more secure and well-structured contract design.

Num of instances: 11

Findings

Click to show findings

['40']

40:     function init(
41:         address dToken_,
42:         address rewardToken_
43:     )
44:         public
45:         initializer
46:     

['63']

63:     function claim()
64:         public
65:     

['110']

110:     function claimableOf(
111:         address holder_
112:     )
113:         public
114:         view
115:         returns(uint reward)
116:     

['125']

125:     function lastestBalance()
126:         public
127:         view
128:         returns(uint)
129:     

['412']

412:     function ethStake(
413:         address payable poolOwner_,
414:         uint duration_,
415:         uint minSPercent_,
416:         uint poolConfigCode_,
417:         uint minWstethA_,
418:         uint wstethA_
419:     )
420:         public
421:         payable
422:         tryPublicMint
423:     

['429']

429:     function dctStake(
430:         uint amount_,
431:         address payable poolOwner_,
432:         uint duration_
433:     )
434:         public
435:         payable
436:         tryPublicMint
437:     

['543']

543:     function lockWithdraw(
544:         bool isEth_,
545:         address payable poolOwner_,
546:         uint amount_,
547:         address payable dest_,
548:         bool isForced_,
549:         uint minEthA_
550:     )
551:         public
552:         tryPublicMint
553:     

['721']

721:     function claimRevenueShareDevTeam()
722:         public
723:     

['175']

175:     function getPoolLockPercent(
176:         address poolOwner_
177:     )
178:         public
179:         view
180:         returns(uint)
181:     

['236']

236:     function getLockedPart(
237:         address poolOwner_,
238:         uint value_
239:     )
240:         public
241:         view
242:         returns(uint)
243:     

['42']

42:     function initLocker(
43:         address accessControl_,
44:         address token_,
45:         address profileCAddr_,
46:         address penaltyAddress_,
47:         address cleanTo_
48:     )
49:         public
50:         initializer
51:     

[Gas-8] Calldata should be used in place of memory function parameters when not mutated

Resolution

In Solidity, calldata should be used in place of memory for function parameters when the function is external. This is because calldata is a non-modifiable, non-persistent area where function arguments are stored, and it's cheaper in terms of gas than memory. It's especially efficient for arrays and complex data types. calldata provides a gas-efficient way to pass data in external function calls, whereas memory is a temporary space that's erased between external function calls. This distinction is crucial for optimizing smart contracts for gas usage and performance.

Num of instances: 1

Findings

Click to show findings

['58']

58:     function initPERC20(
59:         address accessControl_,
60:         bool inPrivateMode_,
61:         string memory name_, // <= FOUND
62:         string memory symbol_
63:     )
64:         public
65:         virtual
66:         initializer
67:     {
68:         initUseAccessControl(accessControl_);
69:         _setInPrivateMode(inPrivateMode_);
70:         _name = name_; // <= FOUND
71:         _symbol = symbol_;
72:     }

[Gas-9] Usage of smaller uint/int types causes overhead

Resolution

When using a smaller int/uint type it first needs to be converted to it's 258 bit counterpart to be operated, this increases the gass cost and thus should be avoided. However it does make sense to use smaller int/uint values within structs provided you pack the struct properly.

Num of instances: 13

Findings

Click to show findings

['146']

146: uint24 private constant POOL_FEE = 100; // <= FOUND

['148']

148: int24 private constant MIN_TICK = -887272; // <= FOUND

['149']

149: int24 private constant MAX_TICK = -MIN_TICK; // <= FOUND

['150']

150: int24 private constant TICK_SPACING = 60; // <= FOUND

['135']

134:     function isContract(address _addr) private view returns (bool){
135:     uint32 size; // <= FOUND
136:     assembly {
137:         size := extcodesize(_addr)
138:     }
139:     return (size > 0);
140:     }

['135']

134:     function isContract(address _addr) private view returns (bool){
135:     uint32 size; // <= FOUND
136:     assembly {
137:         size := extcodesize(_addr)
138:     }
139:     return (size > 0);
140:     }

['168']

164:     function add(
165:         uint tokenId_
166:     )
167:         internal
168:         returns(uint128 liquidity) // <= FOUND
169:     {
170:         uint amount = weth.balanceOf(address(this));
171:         uint wethA = amount / 2;
172:         buyWsteth(amount - wethA);
173:         uint wstethA = wsteth.balanceOf(address(this));
174:         (liquidity, , ) = increaseLiquidityCurrentRange(tokenId_, wethA, wstethA);
175:     }

['179']

177:     function remove(
178:         uint tokenId_,
179:         uint128 decLiqA_ // <= FOUND
180:     )
181:         internal
182:     {
183:         decreaseLiquidityCurrentRange(tokenId_, decLiqA_);
184:         collectAllFees(tokenId_);
185:         uint wstethA = wsteth.balanceOf(address(this));
186:         sellWsteth(wstethA);
187:     }

['191']

189:     function mint()
190:         internal
191:         returns(uint tokenId, uint128 liquidity) // <= FOUND
192:     {
193:         uint amount = weth.balanceOf(address(this));
194:         uint wethA = amount / 2;
195:         buyWsteth(amount - wethA);
196:         uint wstethA = wsteth.balanceOf(address(this));
197:         (tokenId, liquidity, , ) = mintNewPosition(wethA, wstethA);
198:     }

['276']

270:     function mintNewPosition(
271:         uint wethA_,
272:         uint wstethA_
273:     )
274:         internal
275:         returns
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1) // <= FOUND
277:     {
278:         weth.approve(address(nonfungiblePositionManager), wethA_);
279:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
280: 
281:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth);
282:         address token1 = token0 == address(weth) ? address(wsteth) : address(weth);
283:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_;
284:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_;
285: 
286:         INonfungiblePositionManager.MintParams
287:             memory params = INonfungiblePositionManager.MintParams({
288:                 token0: token0,
289:                 token1: token1,
290:                 fee: POOL_FEE,
291:                 tickLower: (MIN_TICK / TICK_SPACING) * TICK_SPACING,
292:                 tickUpper: (MAX_TICK / TICK_SPACING) * TICK_SPACING,
293:                 amount0Desired: amount0ToAdd,
294:                 amount1Desired: amount1ToAdd,
295:                 amount0Min: 0,
296:                 amount1Min: 0,
297:                 recipient: address(this),
298:                 deadline: block.timestamp
299:             });
300: 
301:         (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(
302:             params
303:         );
304:     }

['310']

306:     function increaseLiquidityCurrentRange(
307:         uint tokenId_,
308:         uint wethA_,
309:         uint wstethA_
310:     ) internal returns (uint128 liquidity, uint amount0, uint amount1) { // <= FOUND
311: 
312:         weth.approve(address(nonfungiblePositionManager), wethA_);
313:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
314: 
315:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth);
316:         
317:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_;
318:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_;
319: 
320:         INonfungiblePositionManager.IncreaseLiquidityParams
321:             memory params = INonfungiblePositionManager.IncreaseLiquidityParams({
322:                 tokenId: tokenId_,
323:                 amount0Desired: amount0ToAdd,
324:                 amount1Desired: amount1ToAdd,
325:                 amount0Min: 0,
326:                 amount1Min: 0,
327:                 deadline: block.timestamp
328:             });
329: 
330:         (liquidity, amount0, amount1) = nonfungiblePositionManager.increaseLiquidity(
331:             params
332:         );
333:     }

['356']

349:     function getLiquidity(
350:         uint tokenId_
351:     )
352:         internal
353:         view
354:         returns (uint128)
355:     {
356:         (, , , , , , , uint128 liquidity, , , , ) = nonfungiblePositionManager.positions(tokenId_); // <= FOUND
357:         return liquidity;
358:     }

['362']

360:     function decreaseLiquidityCurrentRange(
361:         uint tokenId_,
362:         uint128 decLiqA_ // <= FOUND
363:     )
364:         internal
365:         returns (uint amount0, uint amount1)
366:     {
367:         
368:         
369:         INonfungiblePositionManager.DecreaseLiquidityParams
370:             memory params = INonfungiblePositionManager.DecreaseLiquidityParams({
371:                 tokenId: tokenId_,
372:                 liquidity: decLiqA_,
373:                 amount0Min: 0,
374:                 amount1Min: 0,
375:                 deadline: block.timestamp
376:             });
377: 
378:         (amount0, amount1) = nonfungiblePositionManager.decreaseLiquidity(params);
379:     }

[Gas-10] Use != 0 instead of > 0

Resolution

Replace spotted instances with != 0 for uints as this uses less gas

Num of instances: 14

Findings

Click to show findings

['56']

56:         if (addedAmount > 0) { // <= FOUND

['96']

96:         if (reward > 0) { // <= FOUND

['404']

404:         if (minWstethA_ > 0) { // <= FOUND

['407']

407:         if (wstethA_ > 0) { // <= FOUND

['146']

146:         if (amount > 0) { // <= FOUND

['86']

86:         if (sAmount > 0) { // <= FOUND

['39']

39:             require(rd > 0, "already unlocked"); // <= FOUND

['51']

51:             require(amount_ > 0 && duration_ > 0, "amount_ = 0 or duration_ = 0"); // <= FOUND

['53']

53:             require(amount_ > 0 || duration_ > 0, "amount_ = 0 and duration_ = 0"); // <= FOUND

['139']

139:     return (size > 0); // <= FOUND

['89']

89:         if (toTransferA > 0) { // <= FOUND

['199']

199:         if (power > 0) { // <= FOUND

['317']

317:         require(winVal > 0, "nothing to claim"); // <= FOUND

['124']

124:         require(mulReward >= 0, "rewardOf,something gone wrong!"); // <= FOUND

[Gas-11] Integer increments by one can be unchecked to save on gas fees

Resolution

Using unchecked increments in Solidity can save on gas fees by bypassing built-in overflow checks, thus optimizing gas usage, but requires careful assessment of potential risks and edge cases to avoid unintended consequences.

Num of instances: 7

Findings

Click to show findings

['61']

52:     function addAdmins(
53:         address[] memory accounts_
54:     )
55:         external
56:         onlyOwner
57:     {
58:         uint i = 0;
59:         while(i < accounts_.length) {
60:             _addAdmin(accounts_[i]);
61:             i++; // <= FOUND
62:         }
63:     }

['74']

65:     function removeAdmins(
66:         address[] memory accounts_
67:     )
68:         external
69:         onlyOwner
70:     {
71:         uint i = 0;
72:         while(i < accounts_.length) {
73:             _removeAdmin(accounts_[i]);
74:             i++; // <= FOUND
75:         }
76:     }

['501']

478:     function earningPulls(
479:         address account_,
480:         address[] memory poolOwners_,
481:         address bountyPullerTo_
482:     )
483:         public
484:         tryPublicMint
485:     {
486:         _dEarning.clean();
487:         _eEarning.clean();
488: 
489:         
490:         _dP2PDistributor.distribute();
491: 
492:         
493:         
494: 
495:         
496:         
497: 
498:         _distributorClaimFor(address(_eDP2PDistributor), account_, address(this));
499:         _distributorClaimFor(address(_dDP2PDistributor), account_, address(_dEarning));
500: 
501:         for(uint i = 0; i < poolOwners_.length; i++) { // <= FOUND
502:             _earningPull(account_, poolOwners_[i]);
503:         }
504: 
505:         if (bountyPullerTo_ == account_) {
506:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
507:         } else {
508:             uint256 amountForPuller = _geth.balanceOf(address(this)) * _bountyPullEarningPercent / LPercentage.DEMI;
509: 
510:             LLido.sellWsteth(amountForPuller);
511:             LLido.wethToEth();
512:             payable(bountyPullerTo_).transfer(address(this).balance);
513: 
514:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
515:         }
516: 
517:         _dEarning.update(account_, true);
518:         _eEarning.update(account_, true);
519: 
520:         _reCalFs(account_);
521:     }

['30']

14:     function initDToken(
15:         address accessControl_,
16:         bool inPrivateMode_,
17:         string memory name_,
18:         string memory symbol_,
19:         address[] memory distributorAddrs_
20:     )
21:         public
22:         initializer
23:     {
24:         initPERC20(accessControl_, inPrivateMode_, name_, symbol_);
25:         _totalDistributors = distributorAddrs_.length;
26:         uint i = 0;
27:         uint n = _totalDistributors;
28:         while (i < n) {
29:             _distributors[i] = IDistributor(distributorAddrs_[i]);
30:             i++; // <= FOUND
31:         }
32:     }

['47']

34:     function _beforeTokenTransfer(
35:         address from_,
36:         address to_,
37:         uint256 amount_
38:     )
39:         internal
40:         virtual
41:         override
42:     {
43:         uint i = 0;
44:         uint n = _totalDistributors;
45:         while (i < n) {
46:             _distributors[i].beforeTokenTransfer(from_, to_, amount_);
47:             i++; // <= FOUND
48:         }
49:     }

['83']

73:     function sendTos(
74:         address[] memory investers_,
75:         uint[] memory amounts_,
76:         uint[] memory vestingDurs_,
77:         uint[] memory vestingPercents_,
78:         uint[] memory cliffs_
79:     )
80:         external
81:         onlyOwner
82:     {
83:         for(uint i = 0; i < investers_.length; i++) { // <= FOUND
84:             _sendTo(investers_[i], amounts_[i], vestingDurs_[i], vestingPercents_[i], cliffs_[i]);
85:         }
86:     }

['153']

136:     function createVote(
137:         address attacker_,
138:         address defender_,
139: 
140:         uint aEthValue_,
141:         uint dEthValue_,
142: 
143:         uint voterPercent_,
144:         uint aQuorum_,
145: 
146:         uint startedAt_,
147:         uint endAt_
148:     )
149:         external
150:         onlyAdmin
151:     {
152:         uint voteId = _totalVotes;
153:         _totalVotes++; // <= FOUND
154:         SVote storage vote = _votes[voteId];
155:         vote.attacker = attacker_;
156:         vote.defender = defender_;
157: 
158:         vote.aEthValue = aEthValue_;
159:         vote.dEthValue = dEthValue_;
160: 
161:         _defenderEarningFreezedOf[defender_] += dEthValue_;
162: 
163:         LPercentage.validatePercent(voterPercent_);
164:         LPercentage.validatePercent(aQuorum_);
165:         vote.voterPercent = voterPercent_;
166:         vote.aQuorum = aQuorum_;
167:         uint inVal = _cashIn();
168:         require(aEthValue_ + dEthValue_ == inVal, "eth value incorrect");
169: 
170:         vote.startedAt = startedAt_;
171:         require(startedAt_ >= block.timestamp, "must start in future");
172:         vote.endAt = endAt_;
173:         require(endAt_ >= startedAt_, "duration not negative");
174:         require(endAt_ < block.timestamp + 365 days, "duration too long");
175:         emit CreateVote(
176:             voteId,
177:             attacker_,
178:             defender_,
179:             aEthValue_,
180:             dEthValue_,
181:             voterPercent_,
182:             aQuorum_,
183:             startedAt_,
184:             endAt_
185:         );
186: 
187:         _addVoter(voteId, attacker_, true);
188:         _addVoter(voteId, defender_, false);
189:     }

[Gas-12] Use byte32 in place of string

Resolution

For strings of 32 char strings and below you can use bytes32 instead as it's more gas efficient

Num of instances: 6

Findings

Click to show findings

['50']

50:     string constant public VERSION = "DEV"; // <= FOUND

['26']

26:     string constant private DIV_TOKEN_NAME = "P2U Dividend Token"; // <= FOUND

['27']

27:     string constant private DIV_TOKEN_SYMBOL = "P2U"; // <= FOUND

['50']

50: string constant public VERSION = "DEV"; // <= FOUND

['26']

26: string constant private DIV_TOKEN_NAME = "P2U Dividend Token"; // <= FOUND

['27']

27: string constant private DIV_TOKEN_SYMBOL = "P2U"; // <= FOUND

[Gas-13] Default bool values are manually reset

Resolution

Using .delete is better than resetting a Solidity variable to its default value manually because it frees up storage space on the Ethereum blockchain, resulting in gas cost savings.

Num of instances: 4

Findings

Click to show findings

['38']

38:     function _removeAdmin(
39:         address account_
40:     )
41:         internal
42:     {
43:         if(_isAdmin[account_]) {
44:             _isAdmin[account_] = false; // <= FOUND
45:             emit RemoveAdmin(account_);
46:         }
47:     }

['85']

85:     function unpause()
86:         external
87:         onlyOwner
88:     {
89:         _isPaused = false; // <= FOUND
90:     }

['89']

89:     function deactiveAthRecord()
90:         external
91:         onlyAdmin
92:     {
93:         _needAthRecord = false; // <= FOUND
94:     }

['95']

95:     function revokeAdmin(
96:         address admin_
97:     )
98:         external
99:     {
100:         address account = msg.sender;
101:         require(_accessControl.isAdmin(admin_), "onlyAdmin");
102:         require(_isApprovedAdmin[account][admin_], "onlyApprovedAdmin");
103:         _isApprovedAdmin[account][admin_] = false; // <= FOUND
104:         emit RevokeAdmin(account, admin_);
105:     }

[Gas-14] Default int values are manually reset

Resolution

Using .delete is better than resetting a Solidity variable to its default value manually because it frees up storage space on the Ethereum blockchain, resulting in gas cost savings.

Num of instances: 8

Findings

Click to show findings

['86']

86:                 rpt = 0; // <= FOUND

['169']

169:             sAmount = 0; // <= FOUND

['205']

205:             vote.powerOf[voter_] = 0; // <= FOUND

['26']

26:         uint i = 0; // <= FOUND

['102']

102:     uint public _isPausedAttack = 0; // <= FOUND

['443']

443:         
444:         uint poolConfigCode = 0; // <= FOUND

['501']

501:         for(uint i = 0; i < poolOwners_.length; i++) { // <= FOUND

['83']

83:         for(uint i = 0; i < investers_.length; i++) { // <= FOUND

[Gas-15] Mappings used within a function more than once should be cached to save gas

Resolution

Cache such mappings and perform operations on them, if operations include modifications to the mapping(s) then remember to equate the mapping to it's cached counterpart at the end

Num of instances: 1

Findings

Click to show findings

['59']

59:     function lock( // <= FOUND
60:         address account_,
61:         address poolOwner_,
62:         uint duration_
63:     )
64:         external
65:         onlyAdmin
66:     {
67:         bytes32 lockId = LLocker.getLockId(account_, poolOwner_);
68:         uint amount = _cashIn();
69:         LLocker.prolong(_lockData[lockId], amount, duration_); // <= FOUND
70:         emit UpdateLockData(account_, poolOwner_, _lockData[lockId]); // <= FOUND
71:     }

[Gas-16] Use assembly to check for the zero address

Resolution

Using assembly for address comparisons in Solidity can save gas because it allows for more direct access to the Ethereum Virtual Machine (EVM), reducing the overhead of higher-level operations. Solidity's high-level abstraction simplifies coding but can introduce additional gas costs. Using assembly for simple operations like address comparisons can be more gas-efficient.

Num of instances: 3

Findings

Click to show findings

['66']

66:     function beforeTokenTransfer(
67:         address from_,
68:         address to_,
69:         uint256 amount_
70:     )
71:         external
72:         onlyDToken
73:     {
74:         _distribute();
75:         if (from_ == to_) {
76:             return;
77:         }
78:         address account;
79:         uint reward;
80: 
81:         account = from_;
82:         if (account != address(0x0)) { // <= FOUND
83:             reward = rewardOf(account);
84:             uint nextBalance = _dToken.balanceOf(account) - amount_;
85:             _setAdjReward(account, reward, reward, nextBalance);
86:         }
87: 
88:         account = to_;
89:         if (account != address(0x0)) { // <= FOUND
90:             reward = rewardOf(account);
91:             uint nextBalance = _dToken.balanceOf(account) + amount_;
92:             _setAdjReward(account, reward, reward, nextBalance);
93:         }
94:     }

['49']

49:     function _updateSponsor(
50:         address account_,
51:         address sponsor_
52:     )
53:         internal
54:     {
55:         require(sponsor_ != address(0x0), "invalid sponsor"); // <= FOUND
56:         SProfile storage profileData = _profileOf[account_];
57:         if (profileData.sponsor == address(0x0)) { // <= FOUND
58:             _initDefaultSPercent(account_);
59:         }
60:         profileData.sponsor = sponsor_;
61:         profileData.sPercent = profileData.nextSPercent;
62:         profileData.updatedAt = block.timestamp;
63:         emit UpdateSponsor(account_, sponsor_, profileData.sPercent);
64:     }

['156']

156:     function getSponsorPart(
157:         address account_,
158:         uint amount_
159:     )
160:         external
161:         view
162:         returns(address sponsor, uint sAmount)
163:     {
164:         SProfile memory profileData = _profileOf[account_];
165:         sponsor = profileData.sponsor;
166:         if (sponsor != address(0x0)) { // <= FOUND
167:             sAmount = LPercentage.getPercentA(amount_, profileData.sPercent);
168:         } else {
169:             sAmount = 0;
170:         }
171:     }

[Gas-17] Some error strings are not descriptive

Resolution

Consider adding more detail to these error strings

Num of instances: 2

Findings

Click to show findings

['652']

641:     function createVote(
642:         address defender_,
643:         uint dEthValue_,
644:         uint voterPercent_,
645:         uint freezeDuration_,
646:         uint minWstethA_,
647:         uint wstethA_
648:     )
649:         external
650:         payable
651:     {
652:         require(_isPausedAttack == 0, "paused"); // <= FOUND
653: 
654:         address attacker = msg.sender;
655:         
656:         _prepareWsteth(minWstethA_, wstethA_);
657:         uint aEthValue = _geth.balanceOf(address(this));
658: 
659:         require(defender_ != address(_devTeam));
660:         require(dEthValue_ >= _minDefenderFund, "dEthValue_ too small");
661:         require(voterPercent_ <= _maxVoterPercent, "voterPercent_ too high");
662:         require(freezeDuration_ >= _minFreezeDuration && freezeDuration_ <= _maxFreezeDuration, "freezeDuration_ invalid");
663:         require(aEthValue <= dEthValue_ && aEthValue * LPercentage.DEMI / dEthValue_ >= _minAttackerFundRate, "aEthValue invalid");
664: 
665:         uint aQuorum = LHelper.calAQuorum(
666:             aEthValue,
667:             dEthValue_,
668:             voterPercent_,
669:             freezeDuration_,
670:             _freezeDurationUnit
671:         );
672: 
673:         _voting.clean();
674:         _eEarning.withdraw(defender_, dEthValue_, address(_voting));
675:         _geth.transfer(address(_voting), aEthValue);
676: 
677:         _dct.transferFrom(attacker, address(0xdead), _attackFee);
678: 
679:         _voting.createVote(
680:             attacker,
681:             defender_,
682:             aEthValue,
683:             dEthValue_,
684:             voterPercent_,
685:             aQuorum,
686:             block.timestamp,
687:             block.timestamp + freezeDuration_
688:         );
689: 
690:         _reCalFs(defender_);
691:     }

['740']

733:     function updateConfigs(
734:         uint[] memory values_
735:     )
736:         external
737:         onlyAdmin
738:     {
739: 
740:         require(values_[0] <= 300, "max 3%"); // <= FOUND
741:         _bountyPullEarningPercent = values_[0];
742: 
743:         _maxBooster = values_[1];
744: 
745:         _maxSponsorAdv = values_[2];
746:         _maxSponsorAfter = values_[3];
747: 
748:         _attackFee = values_[4];
749:         _maxVoterPercent = values_[5];
750:         _minAttackerFundRate = values_[6];
751:         _freezeDurationUnit = values_[7];
752:         _selfStakeAdvantage = values_[8];
753: 
754:         _profileC.setDefaultSPercentConfig(values_[9]);
755:         _isPausedAttack = values_[10];
756: 
757:         _profileC.setMinSPercentConfig(values_[11]);
758: 
759:         _dctTaxPercent = values_[12];
760: 
761:         _minFreezeDuration = values_[13];
762:         _maxFreezeDuration = values_[14];
763: 
764:         _minStakeETHAmount = values_[15];
765:         _minStakeDCTAmount = values_[16];
766: 
767:         _minDefenderFund = values_[17];
768: 
769:         emit AdminUpdateConfig(values_);
770:     }

[Gas-18] Divisions which do not divide by -X cannot overflow or underflow so such operations can be unchecked to save gas

Resolution

Make such found divisions are unchecked when ensured it is safe to do so

Num of instances: 16

Findings

Click to show findings

['34']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     {
34:         uint vestingA = amount_ * vestingPercent_ / 10000; // <= FOUND
35:         uint directA = amount_ - vestingA;
36:         _token.transfer(invester_, directA);
37:         _token.transfer(address(_vester), vestingA);
38:         _vester.lock(invester_, vestingDur_, cliff_);
39:     }

['103']

89:     function publicMint()
90:         external
91:     {
92:         uint mintingA = pendingA();
93:         if (mintingA == 0) {
94:             return;
95:         }
96:         if (totalSupply() + mintingA > MAX_SUPPLY) {
97:             isMintingFinished = true;
98:             return;
99:         }
100:         _mint(_rewardPool, mintingA);
101:         _lastMintAt = block.timestamp;
102:         if (block.timestamp - _lastHalved >= HALVING_INTERVAL) {
103:             _tps = _tps / 2; // <= FOUND
104:             _lastHalved = block.timestamp;
105:         }
106:     }

['171']

164:     function add(
165:         uint tokenId_
166:     )
167:         internal
168:         returns(uint128 liquidity)
169:     {
170:         uint amount = weth.balanceOf(address(this));
171:         uint wethA = amount / 2; // <= FOUND
172:         buyWsteth(amount - wethA);
173:         uint wstethA = wsteth.balanceOf(address(this));
174:         (liquidity, , ) = increaseLiquidityCurrentRange(tokenId_, wethA, wstethA);
175:     }

['194']

189:     function mint()
190:         internal
191:         returns(uint tokenId, uint128 liquidity)
192:     {
193:         uint amount = weth.balanceOf(address(this));
194:         uint wethA = amount / 2; // <= FOUND
195:         buyWsteth(amount - wethA);
196:         uint wstethA = wsteth.balanceOf(address(this));
197:         (tokenId, liquidity, , ) = mintNewPosition(wethA, wstethA);
198:     }

['86']

79:     function calMultiplierForOldAmount(
80:         uint lockTime_
81:     )
82:         internal
83:         pure
84:         returns(uint)
85:     {
86:         uint x = lockTime_ * LPercentage.DEMI / 30 days; // <= FOUND
87:         uint rs = (1300 * x / LPercentage.DEMI); // <= FOUND
88:         return rs;
89:     }

['98']

91:     function calMultiplier(
92:         uint lockTime_
93:     )
94:         internal
95:         pure
96:         returns(uint)
97:     {
98:         uint x = lockTime_ * LPercentage.DEMI / 30 days; // <= FOUND
99:         uint rs = (1300 * x / LPercentage.DEMI) + 8800; // <= FOUND
100:         return rs;
101:     }

['28']

20:     function getPercentA(
21:         uint value,
22:         uint percent
23:     )
24:         internal
25:         pure
26:         returns(uint)
27:     {
28:         return value * percent / DEMI; // <= FOUND
29:     }

['508']

478:     function earningPulls(
479:         address account_,
480:         address[] memory poolOwners_,
481:         address bountyPullerTo_
482:     )
483:         public
484:         tryPublicMint
485:     {
486:         _dEarning.clean();
487:         _eEarning.clean();
488: 
489:         
490:         _dP2PDistributor.distribute();
491: 
492:         
493:         
494: 
495:         
496:         
497: 
498:         _distributorClaimFor(address(_eDP2PDistributor), account_, address(this));
499:         _distributorClaimFor(address(_dDP2PDistributor), account_, address(_dEarning));
500: 
501:         for(uint i = 0; i < poolOwners_.length; i++) {
502:             _earningPull(account_, poolOwners_[i]);
503:         }
504: 
505:         if (bountyPullerTo_ == account_) {
506:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
507:         } else {
508:             uint256 amountForPuller = _geth.balanceOf(address(this)) * _bountyPullEarningPercent / LPercentage.DEMI; // <= FOUND
509: 
510:             LLido.sellWsteth(amountForPuller);
511:             LLido.wethToEth();
512:             payable(bountyPullerTo_).transfer(address(this).balance);
513: 
514:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this)));
515:         }
516: 
517:         _dEarning.update(account_, true);
518:         _eEarning.update(account_, true);
519: 
520:         _reCalFs(account_);
521:     }

['20']

11:     function calEP2PDBalance(
12:         uint fs_,
13:         uint booster_,
14:         uint totalP2UBalance_
15:     )
16:         internal
17:         pure
18:         returns(uint)
19:     {
20:        return fs_ * booster_ * totalP2UBalance_ / LPercentage.DEMIE2; // <= FOUND
21:     }

['41']

23:     function calMintStakingPower(
24:         LLocker.SLock memory oldLockData,
25:         uint lockAmount_,
26:         uint lockTime_,
27:         bool isSelfStake_,
28:         uint selfStakeAdvantage_
29:     )
30:         internal
31:         view
32:         returns(uint)
33:     {
34:         uint rd = LLocker.restDuration(oldLockData);
35:         uint oldALock = oldLockData.amount;
36:         uint dLockForOldA = lockTime_;
37:         uint dLockForStakeA = lockTime_ + rd;
38:         if (lockTime_ == 0) {
39:             require(rd > 0, "already unlocked");
40:         }
41:         uint rs = (oldALock * calMultiplierForOldAmount(dLockForOldA) + lockAmount_ * calMultiplier(dLockForStakeA)) / LPercentage.DEMI; // <= FOUND
42:         if (isSelfStake_) {
43:             rs = rs * selfStakeAdvantage_ / LPercentage.DEMI; // <= FOUND
44:         }
45:         return rs;
46:     }

['78']

68:     function isUnlocked(
69:         SLock memory lockData_,
70:         uint fs_,
71:         bool isPoolOwner_
72:     )
73:         internal
74:         view
75:         returns(bool)
76:     {
77:         uint mFactor = isPoolOwner_ ? 2 * LPercentage.DEMI - fs_ : fs_;
78:         uint duration = lockData_.duration * mFactor / LPercentage.DEMI; // <= FOUND
79:         uint elapsedTime = block.timestamp - lockData_.startedAt;
80:         return elapsedTime >= duration;
81:     }

['93']

83:     function calDuration(
84:         SLock memory lockData_,
85:         uint fs_,
86:         bool isPoolOwner_
87:     )
88:         internal
89:         pure
90:         returns(uint)
91:     {
92:         uint mFactor = isPoolOwner_ ? 2 * LPercentage.DEMI - fs_ : fs_;
93:         uint duration = lockData_.duration * mFactor / LPercentage.DEMI; // <= FOUND
94:         return duration;
95:     }

['81']

69:     function claimFor(
70:         address holder_
71:     )
72:         public
73:     {
74:         distribute();
75: 
76:         uint bal = _dToken.balanceOf(holder_);
77:         SHolder storage holder = holders[holder_];
78:         uint deltaRpt = rpt - holder.lastRpt;
79:         uint reward;
80:         if (holder.regBal > bal) {
81:             reward = deltaRpt * bal / MFACTOR; // <= FOUND
82:             uint unregBal = holder.regBal - bal;
83:             uint removedReward = deltaRpt * unregBal / MFACTOR; // <= FOUND
84:             regSupply -= unregBal;
85:             if (regSupply == 0) {
86:                 rpt = 0;
87:                 
88:             } else {
89:                 rpt += removedReward * MFACTOR / regSupply;
90:             }
91:         } else {
92:             reward = deltaRpt * holder.regBal / MFACTOR; // <= FOUND
93:             regSupply += bal - holder.regBal;
94:         }
95: 
96:         if (reward > 0) {
97:             _cashOut(holder_, reward);
98:             emit Claim(holder_, reward);
99:         }
100: 
101:         if (holder.regBal != bal) {
102:             emit UpdateRegBal(holder_, bal);
103:         }
104: 
105:         holder.regBal = bal;
106:         holder.lastClaimedAt = block.timestamp;
107:         holder.lastRpt = rpt;
108:     }

['121']

110:     function claimableOf(
111:         address holder_
112:     )
113:         public
114:         view
115:         returns(uint reward)
116:     {
117:         uint bal = _dToken.balanceOf(holder_);
118:         SHolder memory holder = holders[holder_];
119:         uint deltaRpt = rpt - holder.lastRpt;
120:         if (holder.regBal > bal) {
121:             reward = deltaRpt * bal / MFACTOR; // <= FOUND
122:         } else {
123:             reward = deltaRpt * holder.regBal / MFACTOR; // <= FOUND
124:         }
125:     }

['125']

114:     function calReward(
115:         uint ptr_,
116:         uint dTokenBalance_,
117:         int256 adjustedReward_
118:     )
119:         public
120:         pure
121:         returns(uint)
122:     {
123:         int256 mulReward = int256(dTokenBalance_ * ptr_) - adjustedReward_;
124:         require(mulReward >= 0, "rewardOf,something gone wrong!");
125:         return uint(mulReward) / MFACTOR; // <= FOUND
126:     }

['291']

270:     function mintNewPosition(
271:         uint wethA_,
272:         uint wstethA_
273:     )
274:         internal
275:         returns
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1)
277:     {
278:         weth.approve(address(nonfungiblePositionManager), wethA_);
279:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
280: 
281:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth);
282:         address token1 = token0 == address(weth) ? address(wsteth) : address(weth);
283:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_;
284:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_;
285: 
286:         INonfungiblePositionManager.MintParams
287:             memory params = INonfungiblePositionManager.MintParams({
288:                 token0: token0,
289:                 token1: token1,
290:                 fee: POOL_FEE,
291:                 tickLower: (MIN_TICK / TICK_SPACING) * TICK_SPACING, // <= FOUND
292:                 tickUpper: (MAX_TICK / TICK_SPACING) * TICK_SPACING, // <= FOUND
293:                 amount0Desired: amount0ToAdd,
294:                 amount1Desired: amount1ToAdd,
295:                 amount0Min: 0,
296:                 amount1Min: 0,
297:                 recipient: address(this),
298:                 deadline: block.timestamp
299:             });
300: 
301:         (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(
302:             params
303:         );
304:     }

[Gas-19] Can transfer 0

Resolution

In Solidity, performing unnecessary operations can consume more gas than needed, leading to cost inefficiencies. For instance, if a transfer function doesn't have a zero amount check and someone calls it with a zero amount, unnecessary gas will be consumed in executing the function, even though the state of the contract remains the same. By implementing a zero amount check, such unnecessary function calls can be avoided, thereby saving gas and making the contract more efficient.

Num of instances: 13

Findings

Click to show findings

['60']

60:     function _cashOut(
61:         address to_,
62:         uint amount_
63:     )
64:         internal
65:     {
66:         _token.transfer(to_, amount_); // <= FOUND
67:         _updateBalance();
68:     }

['95']

95:     function clean()
96:         public
97:         virtual
98:     {
99:         require(_isCleanEnabled, "unable to clean");
100:         uint currentBal = currentBalance();
101:         if (currentBal > _lastestBalance) {
102:             uint amount = currentBal - _lastestBalance;
103:             _token.transfer(_cleanTo, amount); // <= FOUND
104:             emit Clean(amount);
105:         }
106:         _updateBalance();
107:     }

['234']

234:     function _shareDevTeam(
235:         uint amount_
236:     )
237:         internal
238:     {
239:         _geth.transfer(address(_devTeam), amount_); // <= FOUND
240:     }

['243']

243:     function _sharePoolOwner(
244:         uint amount_,
245:         address payable poolOwner_
246:     )
247:         internal
248:     {
249:         _eEarning.clean();
250: 
251:         _geth.transfer(address(_eEarning), amount_); // <= FOUND
252: 
253:         _eEarning.update(poolOwner_, true);
254:     }

['257']

257:     function _sharePoolUser(
258:         uint amount_,
259:         address payable poolOwner_
260:     )
261:         internal
262:     {
263:         IPoolFactory.SPool memory pool = _poolFactory.getPool(poolOwner_);
264:         _geth.transfer(pool.ethDistributor, amount_); // <= FOUND
265:     }

['267']

267:     function _lock(
268:         bool isEth_,
269:         address payable poolOwner_,
270:         uint duration_
271:     )
272:         internal
273:         returns (uint)
274:     {
275: 
276:         if (isEth_) {
277:             uint value = _geth.balanceOf(address(this));
278:             _eLocker.clean();
279:             _geth.transfer(address(_eLocker), value); // <= FOUND
280:             _eLocker.lock(msg.sender, poolOwner_, duration_);
281: 
282:             return value;
283:         } else {
284:             uint value = _dct.balanceOf(address(this));
285:             _dLocker.clean();
286:             _dct.transfer(address(_dLocker), value); // <= FOUND
287:             _dLocker.lock(msg.sender, poolOwner_, duration_);
288: 
289:             return value;
290:         }
291:     }

['429']

429:     function dctStake(
430:         uint amount_,
431:         address payable poolOwner_,
432:         uint duration_
433:     )
434:         public
435:         payable
436:         tryPublicMint
437:     {
438:         _dct.transferFrom(msg.sender, address(this), amount_);
439:         uint taxA = LPercentage.getPercentA(amount_, _dctTaxPercent);
440:         _dct.transfer(address(0xdead), taxA); // <= FOUND
441:         bool isEth = false;
442:         
443:         uint poolConfigCode = 0;
444:         _stake(isEth, msg.sender, poolOwner_, duration_, 0, poolConfigCode);
445:     }

['478']

478:     function earningPulls(
479:         address account_,
480:         address[] memory poolOwners_,
481:         address bountyPullerTo_
482:     )
483:         public
484:         tryPublicMint
485:     {
486:         _dEarning.clean();
487:         _eEarning.clean();
488: 
489:         
490:         _dP2PDistributor.distribute();
491: 
492:         
493:         
494: 
495:         
496:         
497: 
498:         _distributorClaimFor(address(_eDP2PDistributor), account_, address(this));
499:         _distributorClaimFor(address(_dDP2PDistributor), account_, address(_dEarning));
500: 
501:         for(uint i = 0; i < poolOwners_.length; i++) {
502:             _earningPull(account_, poolOwners_[i]);
503:         }
504: 
505:         if (bountyPullerTo_ == account_) {
506:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this))); // <= FOUND
507:         } else {
508:             uint256 amountForPuller = _geth.balanceOf(address(this)) * _bountyPullEarningPercent / LPercentage.DEMI;
509: 
510:             LLido.sellWsteth(amountForPuller);
511:             LLido.wethToEth();
512:             payable(bountyPullerTo_).transfer(address(this).balance); // <= FOUND
513: 
514:             _geth.transfer(address(_eEarning), _geth.balanceOf(address(this))); // <= FOUND
515:         }
516: 
517:         _dEarning.update(account_, true);
518:         _eEarning.update(account_, true);
519: 
520:         _reCalFs(account_);
521:     }

['615']

615:     function earningWithdraw(
616:         bool isEth_,
617:         uint amount_,
618:         address payable dest_,
619:         uint minEthA_
620:         
621:     )
622:         public
623:     {
624:         address account = msg.sender;
625:         
626:         IEarning earning = isEth_ ? _eEarning : _dEarning;
627: 
628:         earning.withdraw(account, amount_, address(this));
629:         
630:         if (isEth_) {
631:             _reCalFs(account);
632:             if (dest_ != address(this)) {
633:                 LLido.allToEth(minEthA_);
634:                 dest_.transfer(address(this).balance); // <= FOUND
635:             }
636:         } else {
637:             _dct.transfer(dest_, _dct.balanceOf(address(this))); // <= FOUND
638:         }
639:     }

['721']

721:     function claimRevenueShareDevTeam()
722:         public
723:     {
724:         address account = msg.sender;
725:         earningWithdrawDevTeam();
726:         _devTeam.claimFor(account, address(this));
727: 
728:         LLido.allToEth(0);
729:         payable(account).transfer(address(this).balance); // <= FOUND
730:     }

['136']

136:     function transfer(address to, uint256 amount) public virtual override returns (bool) {
137:         require(!_inPrivateMode, "_inPrivateMode");
138:         return super.transfer(to, amount); // <= FOUND
139:     }

['25']

25:     function _sendTo(
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     {
34:         uint vestingA = amount_ * vestingPercent_ / 10000;
35:         uint directA = amount_ - vestingA;
36:         _token.transfer(invester_, directA); // <= FOUND
37:         _token.transfer(address(_vester), vestingA); // <= FOUND
38:         _vester.lock(invester_, vestingDur_, cliff_);
39:     }

['63']

63:     function withdraw(
64:         address dest_,
65:         uint amount_
66:     )
67:         external
68:         onlyOwner
69:     {
70:         _token.transfer(dest_, amount_); // <= FOUND
71:     }

[Gas-20] Redundant state variable getters

Resolution

Getters for public state variables are automatically generated so there is no need to code them manually and lose gas

Num of instances: 1

Findings

Click to show findings

['113']

108:     function lastMintAt()
109:         external
110:         view
111:         returns(uint)
112:     {
113:         return _lastMintAt; // <= FOUND
114:     }

[Gas-21] State variables which are not modified within functions should be set as constants or immutable for values set at deployment

Resolution

Set state variables listed below as constant or immutable for values set at deployment. Ensure it is safe to do so

Num of instances: 3

Findings

Click to show findings

['78']

78: uint public _minDuration = 30 days; // <= FOUND

['79']

79: uint public _maxDuration = 720 days; // <= FOUND

['87']

87: uint public _mintingInt = 7; // <= FOUND

[Gas-22] Divisions of powers of 2 can be replaced by a right shift operation to save gas

Resolution

Replace such found divisions with right shift operations when ensured it is safe to do so. NOTE: This only applies to uint variables!

Num of instances: 2

Findings

Click to show findings

['103']

103:             _tps = _tps / 2; // <= FOUND

['171']

171:         uint wethA = amount / 2; // <= FOUND

[Gas-23] Use bitmap to save gas

Resolution

Bitmaps in Solidity are essentially a way of representing a set of boolean values within an integer type variable such as uint256. Each bit in the integer represents a true or false value (1 or 0), thus allowing efficient storage of multiple boolean values.

Bitmaps can save gas in the Ethereum network because they condense a lot of information into a small amount of storage. In Ethereum, storage is one of the most significant costs in terms of gas usage. By reducing the amount of storage space needed, you can potentially save on gas fees.

Here's a quick comparison:

If you were to represent 256 different boolean values in the traditional way, you would have to declare 256 different bool variables. Given that each bool occupies a storage slot and each storage slot costs 20,000 gas to initialize, you would end up paying a considerable amount of gas.

On the other hand, if you were to use a bitmap, you could store these 256 boolean values within a single uint256 variable. In other words, you'd only pay for a single storage slot, resulting in significant gas savings.

However, it's important to note that while bitmaps can provide gas efficiencies, they do add complexity to the code, making it harder to read and maintain. Also, using bitmaps is efficient only when dealing with a large number of boolean variables that are frequently changed or accessed together.

In contrast, the straightforward counterpart to bitmaps would be using arrays or mappings to store boolean values, with each bool value occupying its own storage slot. This approach is simpler and more readable but could potentially be more expensive in terms of gas usage.

Num of instances: 20

Findings

Click to show findings

['33']

33:             _isAdmin[account_] = true; // <= FOUND

['82']

82:         _isPaused = true; // <= FOUND

['424']

424:         bool isEth = true; // <= FOUND

['97']

97:             isMintingFinished = true; // <= FOUND

['49']

49:         bool inPrivateMode = true; // <= FOUND

['90']

90:        poolConfig.isInitialized = true; // <= FOUND

['15']

15:     _isNotInitializable = true; // <= FOUND

['86']

86:         _needAthRecord = true; // <= FOUND

['25']

25:     bool private _inPrivateMode = true; // <= FOUND

['71']

71:         _isCreated[owner_] = true; // <= FOUND

['91']

91:         _isApprovedAdmin[account][admin_] = true; // <= FOUND

['262']

262:         vote.isFinalized = true; // <= FOUND

['312']

312:         vote.isClaimed[voter_] = true; // <= FOUND

['337']

337:         vote.isClosed = true; // <= FOUND

['44']

44:             _isAdmin[account_] = false; // <= FOUND

['89']

89:         _isPaused = false; // <= FOUND

['441']

441:         bool isEth = false; // <= FOUND

['23']

23:     bool public isMintingFinished = false; // <= FOUND

['93']

93:         _needAthRecord = false; // <= FOUND

['103']

103:         _isApprovedAdmin[account][admin_] = false; // <= FOUND

[Gas-24] Consider using OZ EnumerateSet in place of nested mappings

Resolution

Nested mappings and multi-dimensional arrays in Solidity operate through a process of double hashing, wherein the original storage slot and the first key are concatenated and hashed, and then this hash is again concatenated with the second key and hashed. This process can be quite gas expensive due to the double-hashing operation and subsequent storage operation (sstore).

A possible optimization involves manually concatenating the keys followed by a single hash operation and an sstore. However, this technique introduces the risk of storage collision, especially when there are other nested hash maps in the contract that use the same key types. Because Solidity is unaware of the number and structure of nested hash maps in a contract, it follows a conservative approach in computing the storage slot to avoid possible collisions.

OpenZeppelin's EnumerableSet provides a potential solution to this problem. It creates a data structure that combines the benefits of set operations with the ability to enumerate stored elements, which is not natively available in Solidity. EnumerableSet handles the element uniqueness internally and can therefore provide a more gas-efficient and collision-resistant alternative to nested mappings or multi-dimensional arrays in certain scenarios.

Num of instances: 1

Findings

Click to show findings

['71']

71:     mapping(address => mapping(address => bool)) private _isApprovedAdmin; // <= FOUND

[Gas-25] Use selfBalance() in place of address(this).balance

Resolution

In Solidity, using selfBalance instead of address(this).balance is advantageous for gas optimization. address(this).balance performs an external call to retrieve the contract's balance, which costs extra gas. As a resolution, defining a selfBalance state variable that's updated accordingly provides a more gas-efficient approach. Using selfBalance instead of address(this).balance can save around 800 gas per call. This is because address(this).balance makes an EXTBAL operation, which costs 700 gas, and additionally, the operation is a SLOAD, which costs around 800 gas. Please note that these estimates are based on the Ethereum gas schedule and might vary depending on the Ethereum network state and its future upgrades.

Num of instances: 7

Findings

Click to show findings

['512']

512:             payable(bountyPullerTo_).transfer(address(this).balance); // <= FOUND

['574']

573:             
574:             dest_.transfer(address(this).balance); // <= FOUND

['634']

634:                 dest_.transfer(address(this).balance); // <= FOUND

['729']

729:         payable(account).transfer(address(this).balance); // <= FOUND

['233']

233:         require(address(this).balance > minEthBal_, "slipped too high"); // <= FOUND

['264']

264:         uint amount = address(this).balance; // <= FOUND

['275']

275:             to.send(address(this).balance); // <= FOUND

[Gas-26] Using private rather than public for constants and immutables, saves gas

Resolution

Using private visibility for constants and immutables in Solidity instead of public can save gas. This is because private elements are not included in the contract's ABI, reducing the deployment and interaction costs. To achieve better efficiency, it is recommended to use private visibility when external access is not needed.

Num of instances: 6

Findings

Click to show findings

['50']

50: string constant public VERSION = "DEV"; // <= FOUND

['14']

14: uint constant public HALVING_INTERVAL = 7 days; // <= FOUND

['22']

22: uint constant public MAX_SUPPLY = 3111666666 ether; // <= FOUND

['6']

6: uint constant public DEMI = 10000; // <= FOUND

['7']

7: uint constant public DEMIE2 = DEMI * DEMI; // <= FOUND

['8']

8: uint constant public DEMIE3 = DEMIE2 * DEMI; // <= FOUND

[Gas-27] Identical Deployments Should be Replaced with Clone

Resolution

In the context of smart contracts, deploying multiple identical contracts can lead to inefficient use of gas and unnecessarily duplicate code on the blockchain. A more gas-efficient approach is to use a "clone" pattern. By deploying a master contract and then creating clones of it, only the differences between the instances are stored for each clone. This approach leverages the EIP-1167 standard, which defines a minimal proxy contract that points to the implementation contract. Clones can be far cheaper to deploy compared to full instances. So, the resolution is to replace identical deployments with clones, saving on gas and storage space.

Num of instances: 1

Findings

Click to show findings

['58']

58:             addr := create2(0, add(bytecode, 0x20), mload(bytecode), _salt) // <= FOUND
59:             if iszero(extcodesize(addr)) {

[Gas-28] Use assembly to validate msg.sender

Resolution

Utilizing assembly for validating msg.sender can potentially save gas as it allows for more direct and efficient access to Ethereum’s EVM opcodes, bypassing some of the overhead introduced by Solidity’s higher-level abstractions. However, this practice requires deep expertise in EVM, as incorrect implementation can introduce critical vulnerabilities. It is a trade-off between gas efficiency and code safety.

Num of instances: 1

Findings

Click to show findings

['16']

16:         require(msg.sender == address(_dToken), "onlyDToken"); // <= FOUND

[Gas-29] Simple checks for zero uint can be done using assembly to save gas

Resolution

Using assembly for simple zero checks on unsigned integers can save gas due to lower-level, optimized operations.

Resolution: Implement inline assembly with Solidity's assembly block to perform zero checks. Ensure thorough testing and verification, as assembly lacks the safety checks of high-level Solidity, potentially introducing vulnerabilities if not used carefully.

Num of instances: 8

Findings

Click to show findings

['54']

54:         if (regSupply == 0) return; // <= FOUND

['362']

362:             bool isFirstStake = p2UDtoken.totalSupply() == 0; // <= FOUND

['652']

652:         require(_isPausedAttack == 0, "paused"); // <= FOUND

['50']

50:         require(_lastMintAt == 0, "already started"); // <= FOUND

['124']

124:         require(mulReward >= 0, "rewardOf,something gone wrong!"); // <= FOUND

['39']

39:             require(rd > 0, "already unlocked"); // <= FOUND

['139']

139:     return (size > 0); // <= FOUND

['317']

317:         require(winVal > 0, "nothing to claim"); // <= FOUND

[Gas-30] Use Unchecked for Divisions on Constant or Immutable Values

Resolution

When performing divisions in Solidity, the operation costs gas and includes a check for division by zero. However, if you are dividing by a constant or an immutable value that is guaranteed to be non-zero, this check becomes unnecessary, consuming extra gas without adding safety.

Resolution: Utilize the unchecked block for divisions involving constant or immutable values that are assuredly non-zero. This bypasses the additional safety checks, optimizing gas usage. Ensure thorough testing and code reviews are conducted to verify the non-zero condition of the denominator, preventing any potential division by zero errors and maintaining contract safety.

Num of instances: 6

Findings

Click to show findings

['81']

81:             reward = deltaRpt * bal / MFACTOR; // <= FOUND

['83']

83:             uint removedReward = deltaRpt * unregBal / MFACTOR; // <= FOUND

['92']

92:             reward = deltaRpt * holder.regBal / MFACTOR; // <= FOUND

['125']

125:         return uint(mulReward) / MFACTOR; // <= FOUND

['291']

286:         INonfungiblePositionManager.MintParams
287:             memory params = INonfungiblePositionManager.MintParams({
288:                 token0: token0,
289:                 token1: token1,
290:                 fee: POOL_FEE,
291:                 tickLower: (MIN_TICK / TICK_SPACING) * TICK_SPACING, // <= FOUND
292:                 tickUpper: (MAX_TICK / TICK_SPACING) * TICK_SPACING, // <= FOUND
293:                 amount0Desired: amount0ToAdd,
294:                 amount1Desired: amount1ToAdd,
295:                 amount0Min: 0,
296:                 amount1Min: 0,
297:                 recipient: address(this),
298:                 deadline: block.timestamp
299:             });

['28']

28:         return value * percent / DEMI; // <= FOUND

[Gas-31] Using nested if to save gas

Resolution

Using nested if statements instead of logical AND (&&) operators can potentially save gas in Solidity contracts. When a series of conditions are connected with &&, all conditions must be evaluated even if the first one fails. In contrast, nested if statements allow for short-circuiting; if the first condition fails, the rest are skipped, saving gas. This approach is more gas-efficient, especially when dealing with complex or gas-intensive conditions. However, it's crucial to balance gas savings with code readability and maintainability, ensuring that the code remains clear and easy to understand.

Num of instances: 8

Findings

Click to show findings

['145']

145:        if (inDefaultOnlyMode && (poolConfigOf[poolOwner_].code != defaultCode)) { // <= FOUND
146:             _configPool(poolOwner_, defaultOwnerPercent, defaultUserPercent);
147:         }

['32']

32:        if (balance_ > _athBalance && !isContract(account_)) { // <= FOUND
33:             _athBalance = balance_;
34:             emit UpdateAthBalance(account_, balance_);
35:         }

['111']

111:         return _isAdmin[account_] && !_isPaused; // <= FOUND

['662']

662:         require(freezeDuration_ >= _minFreezeDuration && freezeDuration_ <= _maxFreezeDuration, "freezeDuration_ invalid"); // <= FOUND

['663']

663:         require(aEthValue <= dEthValue_ && aEthValue * LPercentage.DEMI / dEthValue_ >= _minAttackerFundRate, "aEthValue invalid"); // <= FOUND

['145']

145:         if (inDefaultOnlyMode && (poolConfigOf[poolOwner_].code != defaultCode)) { // <= FOUND

['51']

51:             require(amount_ > 0 && duration_ > 0, "amount_ = 0 or duration_ = 0"); // <= FOUND

['32']

32:         if (balance_ > _athBalance && !isContract(account_)) { // <= FOUND

[Gas-32] Stack variable cost less than state variables while used in emiting event

Resolution

When emitting events in Solidity, using stack variables (local variables within a function) instead of state variables can lead to significant gas savings. Stack variables reside in memory only for the duration of the function execution and are less costly to access compared to state variables, which are stored on the blockchain. When an event is emitted, accessing these stack variables requires less gas than fetching data from state variables, which involves reading from the contract's storage - a more expensive operation. Thus, for efficiency, prefer using local variables within functions for event emission, especially in functions that are called frequently.

Num of instances: 3

Findings

Click to show findings

['113']

113:         emit ConfigSystem( // <= FOUND
114:             devTeamPercent_,
115:             defaultOwnerPercent_,
116:             defaultUserPercent_,
117:             defaultCode, // <= FOUND
118:             inDefaultOnlyMode_
119:         );

['59']

59:             emit Distribute(addedAmount, regSupply); // <= FOUND

['99']

99:         emit SetSPercent(account_, _defaultSPercent); // <= FOUND

[Gas-33] Stack variable cost less than mappings while used in emiting event

Resolution

When emitting events in Solidity, using stack variables (local variables within a function) instead of mappings can lead to significant gas savings. Stack variables reside in memory only for the duration of the function execution and are less costly to access compared to mappings, which are stored on the blockchain. When an event is emitted, accessing these stack variables requires less gas than fetching data from mappings, which involves reading from the contract's storage - a more expensive operation. Thus, for efficiency, prefer using local variables within functions for event emission, especially in functions that are called frequently.

Num of instances: 2

Findings

Click to show findings

['70']

70:         emit UpdateLockData(account_, poolOwner_, _lockData[lockId]); // <= FOUND

['49']

49:         emit UpdateLockData(account_, _lockData[account_]); // <= FOUND

[Gas-34] Caching global variables is more expensive than using the actual variable

Resolution

In Solidity, caching global or state variables in local variables can be more costly than directly using the state variables. Each assignment to a local variable consumes gas for memory allocation. Accessing state variables directly avoids this overhead, especially if they are only read once, thereby saving gas.

Num of instances: 4

Findings

Click to show findings

['81']

81:         address account = msg.sender; // <= FOUND

['654']

654:         address attacker = msg.sender; // <= FOUND

['63']

63:         address admin = msg.sender; // <= FOUND

['249']

249:         address voter = msg.sender; // <= FOUND

[Gas-35] Inline modifiers used only once

Num of instances: 2

Findings

Click to show findings

['15']

15:     modifier onlyDToken() { // <= FOUND
16:         require(msg.sender == address(_dToken), "onlyDToken"); // <= FOUND
17:         _;
18:     }

['19']

19:     modifier onlyInVotingTime( // <= FOUND
20:         uint voteId_
21:     )
22:     {
23:         require(voteId_ < _totalVotes, "invalid voteId");
24:         SVote storage vote = _votes[voteId_];
25:         require(vote.startedAt <= block.timestamp, "voting not started");
26:         require(vote.endAt >= block.timestamp, "voting already ended");
27:         _;
28:     }

[Gas-36] ++X costs slightly less gas than X++ (same with --)

Resolution

Move the ++/-- action to the left of the variable

Num of instances: 4

Findings

Click to show findings

['30']

30:             i++; // <= FOUND

['501']

501:         for(uint i = 0; i < poolOwners_.length; i++) { // <= FOUND

['83']

83:         for(uint i = 0; i < investers_.length; i++) { // <= FOUND

['153']

153:         _totalVotes++; // <= FOUND

[Gas-37] Solidity versions 0.8.19 and above are more gas efficient

Resolution

Solidity version 0.8.19 introduced a array of gas optimizations which make contracts which use it more efficient. Provided compatability it may be beneficial to upgrade to this version

Num of instances: 1

Findings

Click to show findings

['3']

3: pragma solidity =0.8.8;

[Gas-38] Variables that can be set to immutable

Resolution

The variables are not typically expected to change during the lifespan of the project, as such it makes sense to save gas and set these variables to immutable.

Num of instances: 2

Findings

Click to show findings

['54']

54: string private _symbol; // <= FOUND

['53']

53: string private _name; // <= FOUND

[Gas-39] Internal functions only used once can be inlined to save gas

Resolution

If a internal function is only used once it doesn't make sense to modularise it unless the function which does call the function would be overly long and complex otherwise

Num of instances: 26

Findings

Click to show findings

['27']

27:     function _addAdmin( // <= FOUND
28:         address account_
29:     )
30:         internal
31:     

['38']

38:     function _removeAdmin( // <= FOUND
39:         address account_
40:     )
41:         internal
42:     

['35']

35:     function _setCleanTo( // <= FOUND
36:         address cleanTo_
37:     )
38:         internal
39:     

['234']

234:     function _shareDevTeam( // <= FOUND
235:         uint amount_
236:     )
237:         internal
238:     

['243']

243:     function _sharePoolOwner( // <= FOUND
244:         uint amount_,
245:         address payable poolOwner_
246:     )
247:         internal
248:     

['257']

257:     function _sharePoolUser( // <= FOUND
258:         uint amount_,
259:         address payable poolOwner_
260:     )
261:         internal
262:     

['458']

458:     function _earningPull( // <= FOUND
459:         address account_,
460:         address poolOwner_
461:     )
462:         internal
463:     

['11']

11:     function calEP2PDBalance( // <= FOUND
12:         uint fs_,
13:         uint booster_,
14:         uint totalP2UBalance_
15:     )
16:         internal
17:         pure
18:         returns(uint)
19:     

['61']

61:     function calFs( // <= FOUND
62:         uint earningBalance_,
63:         uint maxEarning_
64:     )
65:         internal
66:         pure
67:         returns(uint)
68:     

['79']

79:     function calMultiplierForOldAmount( // <= FOUND
80:         uint lockTime_
81:     )
82:         internal
83:         pure
84:         returns(uint)
85:     

['103']

103:     function calAQuorum( // <= FOUND
104:         uint aEthValue_,
105:         uint dEthValue_,
106:         uint voterPercent_,
107:         uint freezeDuration_,
108:         uint freezeDurationUnit_
109:     )
110:         internal
111:         pure
112:         returns(uint)
113:     

['270']

270:     function mintNewPosition( // <= FOUND
271:         uint wethA_,
272:         uint wstethA_
273:     )
274:         internal
275:         returns
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1)
277:     

['306']

306:     function increaseLiquidityCurrentRange( // <= FOUND
307:         uint tokenId_,
308:         uint wethA_,
309:         uint wstethA_
310:     ) internal returns (uint128 liquidity, uint amount0, uint amount1) 

['335']

335:     function collectAllFees( // <= FOUND
336:         uint tokenId_
337:     ) internal returns (uint amount0, uint amount1) 

['360']

360:     function decreaseLiquidityCurrentRange( // <= FOUND
361:         uint tokenId_,
362:         uint128 decLiqA_
363:     )
364:         internal
365:         returns (uint amount0, uint amount1)
366:     

['43']

43:     function prolong( // <= FOUND
44:         SLock storage lockData_,
45:         uint amount_,
46:         uint duration_
47:     )
48:         internal
49:     

['68']

68:     function isUnlocked( // <= FOUND
69:         SLock memory lockData_,
70:         uint fs_,
71:         bool isPoolOwner_
72:     )
73:         internal
74:         view
75:         returns(bool)
76:     

['83']

83:     function calDuration( // <= FOUND
84:         SLock memory lockData_,
85:         uint fs_,
86:         bool isPoolOwner_
87:     )
88:         internal
89:         pure
90:         returns(uint)
91:     

['22']

22:     function calBooster( // <= FOUND
23:         uint boostVotePower_,
24:         uint maxBoostVotePower_,
25:         uint maxBooster_
26:     )
27:         pure
28:         internal
29:         returns(uint)
30:     

['87']

87:     function _withdraw( // <= FOUND
88:         address account_,
89:         address poolOwner_,
90:         address dest_,
91:         uint amount_,
92:         bool isForced_
93:     )
94:         internal
95:     

['26']

26:     function _updateAthBalance( // <= FOUND
27:         address account_,
28:         uint balance_
29:     )
30:         internal
31:     

['65']

65:     function _createPool( // <= FOUND
66:         address owner_
67:     )
68:         internal
69:     

['25']

25:     function _sendTo( // <= FOUND
26:         address invester_,
27:         uint amount_,
28:         uint vestingDur_,
29:         uint vestingPercent_,
30:         uint cliff_
31:     )
32:         internal
33:     

['92']

92:     function _initDefaultSPercent( // <= FOUND
93:         address account_
94:     )
95:         internal
96:     

['191']

191:     function _removeVoter( // <= FOUND
192:         uint voteId_,
193:         address voter_
194:     )
195:         internal
196:     

['253']

253:     function _tryFinalize( // <= FOUND
254:         uint voteId_
255:     )
256:         internal
257:     

[Gas-40] Constructors can be marked as payable to save deployment gas

Num of instances: 3

Findings

Click to show findings

['25']

25:     constructor() ERC20("GOAT", "GOAT") {}

['8']

8:     constructor() {
9:         _transferOwnership(msg.sender);
10:     }

['96']

96:     constructor()
97:         ERC20("ignored", "ignored")
98:     {
99:     }

[Gas-41] Same cast is done multiple times

Resolution

Repeatedly casting the same variable to the same type within a function is redundant and can be optimized for better gas efficiency and code readability. Each unnecessary cast operation, while minor, adds to the gas cost and clutters the code. To optimize, the best practice is to perform the cast once and store the result in a temporary variable, which can then be used wherever needed in the function.

Num of instances: 7

Findings

Click to show findings

['641']

641:     function createVote(
642:         address defender_,
643:         uint dEthValue_,
644:         uint voterPercent_,
645:         uint freezeDuration_,
646:         uint minWstethA_,
647:         uint wstethA_
648:     )
649:         external
650:         payable
651:     {
652:         require(_isPausedAttack == 0, "paused");
653: 
654:         address attacker = msg.sender;
655:         
656:         _prepareWsteth(minWstethA_, wstethA_);
657:         uint aEthValue = _geth.balanceOf(address(this));
658: 
659:         require(defender_ != address(_devTeam)); // <= FOUND 'address(_devTeam)'
660:         require(dEthValue_ >= _minDefenderFund, "dEthValue_ too small");
661:         require(voterPercent_ <= _maxVoterPercent, "voterPercent_ too high");
662:         require(freezeDuration_ >= _minFreezeDuration && freezeDuration_ <= _maxFreezeDuration, "freezeDuration_ invalid");
663:         require(aEthValue <= dEthValue_ && aEthValue * LPercentage.DEMI / dEthValue_ >= _minAttackerFundRate, "aEthValue invalid");
664: 
665:         uint aQuorum = LHelper.calAQuorum(
666:             aEthValue,
667:             dEthValue_,
668:             voterPercent_,
669:             freezeDuration_,
670:             _freezeDurationUnit
671:         );
672: 
673:         _voting.clean();
674:         _eEarning.withdraw(defender_, dEthValue_, address(_voting)); // <= FOUND 'address(_voting)'
675:         _geth.transfer(address(_voting), aEthValue); // <= FOUND 'address(_voting)'
676: 
677:         _dct.transferFrom(attacker, address(0xdead), _attackFee);
678: 
679:         _voting.createVote(
680:             attacker,
681:             defender_,
682:             aEthValue,
683:             dEthValue_,
684:             voterPercent_,
685:             aQuorum,
686:             block.timestamp,
687:             block.timestamp + freezeDuration_
688:         );
689: 
690:         _reCalFs(defender_);
691:     }

['712']

712:     function earningWithdrawDevTeam()
713:         public
714:     {
715:         _eEarning.withdraw(address(_devTeam), _eEarning.earningOf(address(_devTeam)), address(_devTeam)); // <= FOUND 'address(_devTeam)'
716:         _dEarning.withdraw(address(_devTeam), _dEarning.earningOf(address(_devTeam)), address(0xdead)); // <= FOUND 'address(_devTeam)'
717: 
718:         _devTeam.distribute();
719:     }

['66']

66:     function beforeTokenTransfer(
67:         address from_,
68:         address to_,
69:         uint256 amount_
70:     )
71:         external
72:         onlyDToken
73:     {
74:         _distribute();
75:         if (from_ == to_) {
76:             return;
77:         }
78:         address account;
79:         uint reward;
80: 
81:         account = from_;
82:         if (account != address(0x0)) { // <= FOUND 'address(0x0)'
83:             reward = rewardOf(account);
84:             uint nextBalance = _dToken.balanceOf(account) - amount_;
85:             _setAdjReward(account, reward, reward, nextBalance);
86:         }
87: 
88:         account = to_;
89:         if (account != address(0x0)) { // <= FOUND 'address(0x0)'
90:             reward = rewardOf(account);
91:             uint nextBalance = _dToken.balanceOf(account) + amount_;
92:             _setAdjReward(account, reward, reward, nextBalance);
93:         }
94:     }

['270']

270:     function mintNewPosition(
271:         uint wethA_,
272:         uint wstethA_
273:     )
274:         internal
275:         returns
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1)
277:     {
278:         weth.approve(address(nonfungiblePositionManager), wethA_);
279:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
280: 
281:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth); // <= FOUND 'address(weth)'
282:         address token1 = token0 == address(weth) ? address(wsteth) : address(weth); // <= FOUND 'address(weth)'
283:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_; // <= FOUND 'address(weth)'
284:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_; // <= FOUND 'address(weth)'
285: 
286:         INonfungiblePositionManager.MintParams
287:             memory params = INonfungiblePositionManager.MintParams({
288:                 token0: token0,
289:                 token1: token1,
290:                 fee: POOL_FEE,
291:                 tickLower: (MIN_TICK / TICK_SPACING) * TICK_SPACING,
292:                 tickUpper: (MAX_TICK / TICK_SPACING) * TICK_SPACING,
293:                 amount0Desired: amount0ToAdd,
294:                 amount1Desired: amount1ToAdd,
295:                 amount0Min: 0,
296:                 amount1Min: 0,
297:                 recipient: address(this),
298:                 deadline: block.timestamp
299:             });
300: 
301:         (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint(
302:             params
303:         );
304:     }

['306']

306:     function increaseLiquidityCurrentRange(
307:         uint tokenId_,
308:         uint wethA_,
309:         uint wstethA_
310:     ) internal returns (uint128 liquidity, uint amount0, uint amount1) {
311: 
312:         weth.approve(address(nonfungiblePositionManager), wethA_);
313:         wsteth.approve(address(nonfungiblePositionManager), wstethA_);
314: 
315:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth); // <= FOUND 'address(weth)'
316:         
317:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_; // <= FOUND 'address(weth)'
318:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_; // <= FOUND 'address(weth)'
319: 
320:         INonfungiblePositionManager.IncreaseLiquidityParams
321:             memory params = INonfungiblePositionManager.IncreaseLiquidityParams({
322:                 tokenId: tokenId_,
323:                 amount0Desired: amount0ToAdd,
324:                 amount1Desired: amount1ToAdd,
325:                 amount0Min: 0,
326:                 amount1Min: 0,
327:                 deadline: block.timestamp
328:             });
329: 
330:         (liquidity, amount0, amount1) = nonfungiblePositionManager.increaseLiquidity(
331:             params
332:         );
333:     }

['65']

65:     function _createPool(
66:         address owner_
67:     )
68:         internal
69:     {
70:         require(!_isCreated[owner_], "already created");
71:         _isCreated[owner_] = true;
72: 
73:         uint salt = uint256(uint160(owner_));
74:         SPool storage pool = _pools[owner_];
75:         pool.dToken = _deploy(_dTokenBytecode, salt);
76:         pool.ethDistributor = _deploy(_distributorBytecode, salt);
77:         pool.dctDistributor = _deploy(_distributorBytecode, salt + 1);
78: 
79:         address[] memory poolDistributorAddrs = new address[](2);
80:         poolDistributorAddrs[0] = pool.ethDistributor;
81:         poolDistributorAddrs[1] = pool.dctDistributor;
82:         IDToken(pool.dToken).initDToken(
83:             address(_accessControl), // <= FOUND 'address(_accessControl)'
84:             _inPrivateMode,
85:             DIV_TOKEN_NAME,
86:             DIV_TOKEN_SYMBOL,
87:             poolDistributorAddrs
88:         );
89: 
90:         IDistributor(pool.ethDistributor).initDistributor(
91:             address(_accessControl), // <= FOUND 'address(_accessControl)'
92:             pool.dToken,
93:             _geth
94:         );
95: 
96:         IDistributor(pool.dctDistributor).initDistributor(
97:             address(_accessControl), // <= FOUND 'address(_accessControl)'
98:             pool.dToken,
99:             _dct
100:         );
101:         emit CreatePool(owner_, pool);
102:     }

['49']

49:     function _updateSponsor(
50:         address account_,
51:         address sponsor_
52:     )
53:         internal
54:     {
55:         require(sponsor_ != address(0x0), "invalid sponsor"); // <= FOUND 'address(0x0)'
56:         SProfile storage profileData = _profileOf[account_];
57:         if (profileData.sponsor == address(0x0)) { // <= FOUND 'address(0x0)'
58:             _initDefaultSPercent(account_);
59:         }
60:         profileData.sponsor = sponsor_;
61:         profileData.sPercent = profileData.nextSPercent;
62:         profileData.updatedAt = block.timestamp;
63:         emit UpdateSponsor(account_, sponsor_, profileData.sPercent);
64:     }

[Gas-42] Assigning to structs can be more efficient

Resolution

Rather defining the struct in a single line, it is more efficient to declare an empty struct and then assign each struct element individually. This can net quite a large gas saving of 130 per instance.

Num of instances: 5

Findings

Click to show findings

['270']

270:     function mintNewPosition(
271:         uint wethA_, // <= FOUND
272:         uint wstethA_ // <= FOUND
273:     )
274:         internal // <= FOUND
275:         returns // <= FOUND
276:         (uint tokenId, uint128 liquidity, uint amount0, uint amount1) // <= FOUND
277:     {
278:         weth.approve(address(nonfungiblePositionManager), wethA_); // <= FOUND
279:         wsteth.approve(address(nonfungiblePositionManager), wstethA_); // <= FOUND
280: 
281:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth); // <= FOUND
282:         address token1 = token0 == address(weth) ? address(wsteth) : address(weth); // <= FOUND
283:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_; // <= FOUND
284:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_; // <= FOUND
285: 
286:         INonfungiblePositionManager.MintParams // <= FOUND
287:             memory params = INonfungiblePositionManager.MintParams({ // <= FOUND
288:                 token0: token0, // <= FOUND
289:                 token1: token1, // <= FOUND
290:                 fee: POOL_FEE, // <= FOUND
291:                 tickLower: (MIN_TICK / TICK_SPACING) * TICK_SPACING, // <= FOUND
292:                 tickUpper: (MAX_TICK / TICK_SPACING) * TICK_SPACING, // <= FOUND
293:                 amount0Desired: amount0ToAdd, // <= FOUND
294:                 amount1Desired: amount1ToAdd, // <= FOUND
295:                 amount0Min: 0, // <= FOUND
296:                 amount1Min: 0, // <= FOUND
297:                 recipient: address(this), // <= FOUND
298:                 deadline: block.timestamp // <= FOUND
299:             }); // <= FOUND
300: 
301:         (tokenId, liquidity, amount0, amount1) = nonfungiblePositionManager.mint( // <= FOUND
302:             params // <= FOUND
303:         ); // <= FOUND
304:     }

['306']

306:     function increaseLiquidityCurrentRange(
307:         uint tokenId_, // <= FOUND
308:         uint wethA_, // <= FOUND
309:         uint wstethA_ // <= FOUND
310:     ) internal returns (uint128 liquidity, uint amount0, uint amount1) {
311: 
312:         weth.approve(address(nonfungiblePositionManager), wethA_); // <= FOUND
313:         wsteth.approve(address(nonfungiblePositionManager), wstethA_); // <= FOUND
314: 
315:         address token0 = address(weth) < address(wsteth) ? address(weth) : address(wsteth); // <= FOUND
316:          // <= FOUND
317:         uint amount0ToAdd = token0 == address(weth) ? wethA_ : wstethA_; // <= FOUND
318:         uint amount1ToAdd = token0 == address(weth) ? wstethA_ : wethA_; // <= FOUND
319: 
320:         INonfungiblePositionManager.IncreaseLiquidityParams // <= FOUND
321:             memory params = INonfungiblePositionManager.IncreaseLiquidityParams({ // <= FOUND
322:                 tokenId: tokenId_, // <= FOUND
323:                 amount0Desired: amount0ToAdd, // <= FOUND
324:                 amount1Desired: amount1ToAdd, // <= FOUND
325:                 amount0Min: 0, // <= FOUND
326:                 amount1Min: 0, // <= FOUND
327:                 deadline: block.timestamp // <= FOUND
328:             }); // <= FOUND
329: 
330:         (liquidity, amount0, amount1) = nonfungiblePositionManager.increaseLiquidity( // <= FOUND
331:             params // <= FOUND
332:         ); // <= FOUND
333:     }

['335']

335:     function collectAllFees(
336:         uint tokenId_ // <= FOUND
337:     ) internal returns (uint amount0, uint amount1) {
338:         INonfungiblePositionManager.CollectParams // <= FOUND
339:             memory params = INonfungiblePositionManager.CollectParams({ // <= FOUND
340:                 tokenId: tokenId_, // <= FOUND
341:                 recipient: address(this), // <= FOUND
342:                 amount0Max: type(uint128).max, // <= FOUND
343:                 amount1Max: type(uint128).max // <= FOUND
344:             }); // <= FOUND
345: 
346:         (amount0, amount1) = nonfungiblePositionManager.collect(params); // <= FOUND
347:     }

['360']

360:     function decreaseLiquidityCurrentRange(
361:         uint tokenId_, // <= FOUND
362:         uint128 decLiqA_ // <= FOUND
363:     )
364:         internal // <= FOUND
365:         returns (uint amount0, uint amount1) // <= FOUND
366:     {
367:          // <= FOUND
368:          // <= FOUND
369:         INonfungiblePositionManager.DecreaseLiquidityParams // <= FOUND
370:             memory params = INonfungiblePositionManager.DecreaseLiquidityParams({ // <= FOUND
371:                 tokenId: tokenId_, // <= FOUND
372:                 liquidity: decLiqA_, // <= FOUND
373:                 amount0Min: 0, // <= FOUND
374:                 amount1Min: 0, // <= FOUND
375:                 deadline: block.timestamp // <= FOUND
376:             }); // <= FOUND
377: 
378:         (amount0, amount1) = nonfungiblePositionManager.decreaseLiquidity(params); // <= FOUND
379:     }

['381']

381:     function swapExactInputSingleHop(
382:         address tokenIn, // <= FOUND
383:         address tokenOut, // <= FOUND
384:         uint amountIn // <= FOUND
385:     )
386:         internal // <= FOUND
387:         returns (uint amountOut) { // <= FOUND
388:         ISwapRouter.ExactInputSingleParams memory params = ISwapRouter // <= FOUND
389:             .ExactInputSingleParams({ // <= FOUND
390:                 tokenIn: tokenIn, // <= FOUND
391:                 tokenOut: tokenOut, // <= FOUND
392:                 fee: POOL_FEE, // <= FOUND
393:                 recipient: address(this), // <= FOUND
394:                  // <= FOUND
395:                 amountIn: amountIn, // <= FOUND
396:                 amountOutMinimum: 0, // <= FOUND
397:                 sqrtPriceLimitX96: 0 // <= FOUND
398:             }); // <= FOUND
399: 
400:         amountOut = router.exactInputSingle(params); // <= FOUND
401:     }

[Gas-43] Internal functions never used once can be removed

Resolution

Internal functions which are never used use unnecessary gas and should be safely removed.

Num of instances: 2

Findings

Click to show findings

['155']

155:     function createPairForTestnet( // <= FOUND
156:         address wstethAddr_
157:     )
158:         internal
159:     

['349']

349:     function getLiquidity( // <= FOUND
350:         uint tokenId_
351:     )
352:         internal
353:         view
354:         returns (uint128)
355:     

[Gas-44] Only emit event in setter function if the state variable was changed

Resolution

Emitting events in setter functions of smart contracts only when state variables change saves gas. This is because emitting events consumes gas, and unnecessary events, where no actual state change occurs, lead to wasteful consumption.

Num of instances: 9

Findings

Click to show findings

['76']

76:     function setSPercent( // <= FOUND
77:         uint sPercent_
78:     )
79:         external
80:     {
81:         address account = msg.sender;
82:         LPercentage.validatePercent(sPercent_);
83:         require(sPercent_ >= _minSPercent, "sPercent_ invalid");
84:         SProfile storage profileData = _profileOf[account];
85:         profileData.nextSPercent = sPercent_;
86:         if (sPercent_ >  profileData.sPercent) {
87:             _updateSponsor(account, profileData.sponsor);
88:         }
89:         emit SetSPercent(account, sPercent_); // <= FOUND
90:     }

['96']

96:     function _setAdjReward( // <= FOUND
97:         address account_,
98:         uint prevReward,
99:         uint reward_,
100:         uint dTokenBalance_
101:     )
102:         internal
103:     {
104:         _adjustedRewardOf[account_] = int256(dTokenBalance_ * _ptr) - int256(reward_ * MFACTOR);
105:         emit SetReward(account_, prevReward, reward_); // <= FOUND
106:     }

['127']

127:     function _setInPrivateMode( // <= FOUND
128:         bool inPrivateMode_
129:     )
130:         internal
131:     {
132:         _inPrivateMode = inPrivateMode_;
133:         emit SetInPrivateMode(inPrivateMode_); // <= FOUND
134:     }

['733']

733:     function updateConfigs( // <= FOUND
734:         uint[] memory values_
735:     )
736:         external
737:         onlyAdmin
738:     {
739: 
740:         require(values_[0] <= 300, "max 3%");
741:         _bountyPullEarningPercent = values_[0];
742: 
743:         _maxBooster = values_[1];
744: 
745:         _maxSponsorAdv = values_[2];
746:         _maxSponsorAfter = values_[3];
747: 
748:         _attackFee = values_[4];
749:         _maxVoterPercent = values_[5];
750:         _minAttackerFundRate = values_[6];
751:         _freezeDurationUnit = values_[7];
752:         _selfStakeAdvantage = values_[8];
753: 
754:         _profileC.setDefaultSPercentConfig(values_[9]);
755:         _isPausedAttack = values_[10];
756: 
757:         _profileC.setMinSPercentConfig(values_[11]);
758: 
759:         _dctTaxPercent = values_[12];
760: 
761:         _minFreezeDuration = values_[13];
762:         _maxFreezeDuration = values_[14];
763: 
764:         _minStakeETHAmount = values_[15];
765:         _minStakeDCTAmount = values_[16];
766: 
767:         _minDefenderFund = values_[17];
768: 
769:         emit AdminUpdateConfig(values_); // <= FOUND
770:     }

['122']

122:     function updateFsOf( // <= FOUND
123:         address account_,
124:         uint fs_
125:     )
126:         external
127:         onlyAdmin
128:     {
129:         SProfile storage profileData = _profileOf[account_];
130:         profileData.ifs = LProfile.invertOf(fs_);
131:         emit UpdateFsOf(account_, fs_); // <= FOUND
132:     }

['134']

134:     function updateBoosterOf( // <= FOUND
135:         address account_,
136:         uint booster_
137:     )
138:         external
139:         onlyAdmin
140:     {
141:         SProfile storage profileData = _profileOf[account_];
142:         profileData.bonusBooster = booster_ - LPercentage.DEMI;
143:         emit UpdateBoosterOf(account_, booster_); // <= FOUND
144:     }

['53']

53:     function _updateMaxEarning( // <= FOUND
54:         address account_,
55:         uint maxEarning_
56:     )
57:         internal
58:     {
59:         _maxEarningOf[account_] = maxEarning_;
60:         emit UpdateMaxEarning(account_, maxEarning_); // <= FOUND
61:     }

['121']

121:     function _updatePenaltyAddress( // <= FOUND
122:         address penaltyAddress_
123:     )
124:         internal
125:     {
126:         _penaltyAddress = penaltyAddress_;
127:         emit UpdatePenaltyAddress(penaltyAddress_); // <= FOUND
128:     }

['26']

26:     function _updateAthBalance( // <= FOUND
27:         address account_,
28:         uint balance_
29:     )
30:         internal
31:     {
32:         if (balance_ > _athBalance && !isContract(account_)) {
33:             _athBalance = balance_;
34:             emit UpdateAthBalance(account_, balance_); // <= FOUND
35:         }
36:     }

[Gas-45] It is a waste of GAS to emit variable literals

Resolution

Emitting variable literals (true, false, 'hello', 1 etc...) in events is inefficient, as it consumes extra gas without providing added value. These literals are fixed values that can be accessed or hardcoded elsewhere in the smart contract or application, making their inclusion in events redundant and an unnecessary drain on resources during transaction execution.

Num of instances: 1

Findings

Click to show findings

['384']

384:             emit Stake(isEth_, poolOwner_, account_, value, duration_, powerMinted, false); // <= FOUND

[Gas-46] Use OZ Array.unsafeAccess() to avoid repeated array length checks

Resolution

The OpenZeppelin Array.unsafeAccess() method is a optimization strategy for Solidity, aimed at reducing gas costs by bypassing automatic length checks on storage array accesses. In Solidity, every access to an array element involves a hidden gas cost due to a length check, ensuring that accesses do not exceed the array bounds. However, if a developer has already verified the array's bounds earlier in the function or knows through logic that the access is safe, directly accessing the array elements without redundant length checks can save gas. This approach requires careful consideration to avoid out-of-bounds errors, as it trades off safety checks for efficiency.

Num of instances: 6

Findings

Click to show findings

['60']

60:             _addAdmin(accounts_[i]); // <= FOUND

['73']

73:             _removeAdmin(accounts_[i]); // <= FOUND

['502']

502:             _earningPull(account_, poolOwners_[i]); // <= FOUND

['29']

29:             _distributors[i] = IDistributor(distributorAddrs_[i]); // <= FOUND

['46']

46:             _distributors[i].beforeTokenTransfer(from_, to_, amount_); // <= FOUND

['84']

84:             _sendTo(investers_[i], amounts_[i], vestingDurs_[i], vestingPercents_[i], cliffs_[i]); // <= FOUND

[Gas-47] Call msg.XYZ directly rather than caching the value to save gas

Num of instances: 4

Findings

Click to show findings

['81']

81:         address account = msg.sender; // <= FOUND

['654']

654:         address attacker = msg.sender; // <= FOUND

['63']

63:         address admin = msg.sender; // <= FOUND

['249']

249:         address voter = msg.sender; // <= FOUND

[Gas-48] Use of memory instead of storage for struct/array state variables

Resolution

In Solidity, choosing between memory and storage for variables, especially when dealing with structs or arrays, is crucial for optimizing gas costs. Variables declared as storage are pointers to the blockchain data, leading to lower gas consumption when fields are accessed or modified, as they don't require reading the entire structure. In contrast, memory variables copy the entire struct or array from storage, incurring significant gas costs, especially for large or complex structures. Therefore, use storage for state variables or when working within functions to manipulate existing contract data. Reserve memory for temporary data or when data needs to be passed to external functions as copies, ensuring efficient use of gas and avoiding unnecessary costs.

Num of instances: 4

Findings

Click to show findings

['118']

118:         SHolder memory holder = holders[holder_]; // <= FOUND

['182']

182:         SPoolConfig memory poolConfig = poolConfigOf[poolOwner_]; // <= FOUND

['116']

116:         LLocker.SLock memory lockData = _lockData[account_]; // <= FOUND

['164']

164:         SProfile memory profileData = _profileOf[account_]; // <= FOUND

[Gas-49] Public functions not called internally

Resolution

Public functions that aren't used internally in Solidity contracts should be made external to optimize gas usage and improve contract efficiency. External functions can only be called from outside the contract, and their arguments are directly read from the calldata, which is more gas-efficient than loading them into memory, as is the case for public functions. By using external visibility, developers can reduce gas consumption for external calls and ensure that the contract operates more cost-effectively for users. Moreover, setting the appropriate visibility level for functions also enhances code readability and maintainability, promoting a more secure and well-structured contract design.

Num of instances: 11

Findings

Click to show findings

['40']

40:     function init(
41:         address dToken_,
42:         address rewardToken_
43:     )
44:         public
45:         initializer
46:     

['63']

63:     function claim()
64:         public
65:     

['110']

110:     function claimableOf(
111:         address holder_
112:     )
113:         public
114:         view
115:         returns(uint reward)
116:     

['125']

125:     function lastestBalance()
126:         public
127:         view
128:         returns(uint)
129:     

['412']

412:     function ethStake(
413:         address payable poolOwner_,
414:         uint duration_,
415:         uint minSPercent_,
416:         uint poolConfigCode_,
417:         uint minWstethA_,
418:         uint wstethA_
419:     )
420:         public
421:         payable
422:         tryPublicMint
423:     

['429']

429:     function dctStake(
430:         uint amount_,
431:         address payable poolOwner_,
432:         uint duration_
433:     )
434:         public
435:         payable
436:         tryPublicMint
437:     

['543']

543:     function lockWithdraw(
544:         bool isEth_,
545:         address payable poolOwner_,
546:         uint amount_,
547:         address payable dest_,
548:         bool isForced_,
549:         uint minEthA_
550:     )
551:         public
552:         tryPublicMint
553:     

['721']

721:     function claimRevenueShareDevTeam()
722:         public
723:     

['175']

175:     function getPoolLockPercent(
176:         address poolOwner_
177:     )
178:         public
179:         view
180:         returns(uint)
181:     

['236']

236:     function getLockedPart(
237:         address poolOwner_,
238:         uint value_
239:     )
240:         public
241:         view
242:         returns(uint)
243:     

['42']

42:     function initLocker(
43:         address accessControl_,
44:         address token_,
45:         address profileCAddr_,
46:         address penaltyAddress_,
47:         address cleanTo_
48:     )
49:         public
50:         initializer
51:     
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment