Platform: Code4rena
Start Date: 17/03/2022
Pot Size: $30,000 USDC
Total HM: 8
Participants: 43
Period: 3 days
Judge: gzeon
Total Solo HM: 5
Id: 100
League: ETH
Rank: 19/43
Findings: 2
Award: $130.66
π Selected for report: 0
π Solo Findings: 0
π Selected for report: defsec
Also found by: 0x1f8b, 0xDjango, 0xNazgul, 0xkatana, 0xwags, CertoraInc, Funen, GeekyLumberjack, GreyArt, IllIllI, Kenshin, Ruhum, TerrierLover, WatchPug, berndartmueller, bugwriter001, cccz, cmichel, csanuragjain, hake, kenta, kirk-baird, leastwood, minhquanym, oyc_109, peritoflores, rayn, remora, rfa, robee, saian, samruna, sorrynotsorry, wuwe1
101.302 USDC - $101.30
It's best practice for a contract to inherit from it's interface. This improves the contract's clarity and makes sure the contract implementation complies with the defined interface.
contract LongShortToken is ILongShortToken, ERC20Burnable, Ownable { // @audit-info add interface ILongShortToken ... }
Inherit from the missing interface or contract.
1e10
instead of using many zerosFor better readability and to prevent any issues, use the scientific notation 1e10
instead of e.g. 1000000
Collateral.FEE_DENOMINATOR: 1000000
-> use 1e6
<br/>
PrePOMarket.FEE_DENOMINATOR: 1000000
-> use 1e6
<br/>
true
or false
Boolean constants can be used directly and do not need to be compare to true or false.
Remove the equality to the boolean constant.
Code contains empty block. See https://protofire.github.io/solhint/docs/rules/best-practises/no-empty-blocks.html
AccountAccessController.constructor()
Remove empty code block
ICollateral
instead of IERC20
Use more specific interface ICollateral
instead of the general interface IERC20
for code clarity.
PrePOMarket._collateral PrePOMarket.constructor()
Change variable type IERC20
to ICollateral
:
ICollateral private immutable _collateral;
and
_collateral = ICollateral(_newCollateral);
Collateral.deposit()
In the comments, right next to the actual implementation of the calculation of shares, the formula is wrong. The implementation itself is correct.
Wrong calculation:
/** * # of shares owed = amount deposited / cost per share, cost per * share = total supply / total value. */
Correct calculation:
/** * # of shares owed = amount deposited / cost per share, cost per * share = total value / total supply. @audit-info swapped total value with total supply */
Fix comment to prevent confusion with actual implementation.
Zero-address checks are a best-practice for input validation of critical address parameters. While the codebase applies this to most most cases, there are many places where this is missing in constructors and setters.
Impact: Accidental use of zero-addresses may result in exceptions, burn fees/tokens or force redeployment of contracts.
Collateral.initialize()#_newTreasury
Add zero-address checks, e.g.:
require(address(_newTreasury) != address(0), "Zero address");
PrePOMarketFactory.createMarket()
is different than defined in interface IPrePOMarketFactory.createMarket()
The order of parameters in the contract implementation PrePOMarketFactory
is different than how parameters are defined in the interface.
PrePOMarketFactory.createMarket()
Either change the order of parameters in the interface IPrePOMarketFactory
to match the implementation or update the implementation.
IPrePOMarketFactory.createMarket()
function createMarket( string memory _tokenNameSuffix, string memory _tokenSymbolSuffix, address _governance, address _collateral, uint256 _floorLongPrice, uint256 _ceilingLongPrice, uint256 _floorValuation, uint256 _ceilingValuation, uint256 _mintingFee, uint256 _redemptionFee, uint256 _expiryTime ) external override onlyOwner nonReentrant {
PrePOMarketFactory.createMarket()
function createMarket( string memory tokenNameSuffix, string memory tokenSymbolSuffix, address collateral, // @audit-info `collateral` and `governance` parameters are switched compared to how they are defined in the interface address governance, uint256 floorLongPrice, uint256 ceilingLongPrice, uint256 floorValuation, uint256 ceilingValuation, uint256 mintingFee, uint256 redemptionFee, uint256 expiryTime ) external override onlyOwner nonReentrant {
#0 - ramenforbreakfast
2022-03-24T06:16:27Z
Non-Critical Contract implementations should inherit their interface - is a valid consideration of severity 0 Use scientific notation 1e10 instead of using many zeros - is an incorrect version of a duplicate suggestion. Boolean constants can be used directly and do not need to be compare to true or false - duplicate of #5 Remove empty blocks of code - duplicate of #5 Use interface ICollateral instead of IERC20 - is a valid consideration of severity 0
Low-Risk Wrong calculation of shares mentioned in comment of Collateral.deposit() - duplicate of #40 Zero-address checks are missing - duplicate of #35 IPrePOMarketFactory wrong interface - duplicate of #30
Since the only suggestions that are not duplicates are of severity 0, will request this as needing a lower severity.
29.3575 USDC - $29.36
Removing unused function parameters can save gas. And it improves code clarity.
IHook.hook()
DepositHook.hook()
WithdrawHook.hook()
Remove unused function parameter initialAmount
.
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.
Caching the array length in the stack saves around 3 gas per iteration.
AccountAccessController.allowAccounts()
AccountAccessController.blockAccounts()
AccountAccessController.allowAccounts()
uint length = _accounts.length; for (uint256 _i = 0; _i < length; _i++) { _allowedAccounts[_allowedAccountsIndex][_accounts[_i]] = true; emit AccountAllowed(_accounts[_i]); }
AccountAccessController.blockAccounts()
uint length = _accounts.length; for (uint256 _i = 0; _i < length; _i++) { _blockedAccounts[_blockedAccountsIndex][_accounts[_i]] = true; emit AccountBlocked(_accounts[_i]); }
Reading the same variable from storage multiple times consumes unnecessary gas because SLOADs are expensive.
AccountAccessController.allowAccounts()
Repetitive storage access of variable: _accounts[_i]
1st access - L45
2nd access - L46
AccountAccessController.blockAccounts()
Repetitive storage access of variable: _accounts[_i]
1st access - L56
2nd access - L57
Read the variable from storage once and store it in a temporary variable in memory for repetitive read access.
Removing unused named return variables can reduce gas usage and improve code clarity.
Remove the unused named return variables or use them instead of creating additional variables.
L98 unnecessary named returns _newLongToken
& _newShortToken
#0 - ramenforbreakfast
2022-03-24T06:23:24Z
Unused parameter - duplicate of #4 Caching recs duplicate of #5 and #18 Unused named returns is valid and we will take into consideration.