Skip to content

Instantly share code, notes, and snippets.

@ChaseTheLight01
Created March 24, 2024 06:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • 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<