Platform: Code4rena
Start Date: 05/01/2023
Pot Size: $90,500 USDC
Total HM: 55
Participants: 103
Period: 14 days
Judge: Picodes
Total Solo HM: 18
Id: 202
League: ETH
Rank: 41/103
Findings: 2
Award: $290.13
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: ladboy233
Also found by: 0x1f8b, 0xAgro, 0xSmartContract, 0xbepresent, 0xkato, Aymen0909, CodingNameKiki, Cryptor, Deekshith99, Deivitto, HE1M, IllIllI, Kaysoft, Koolex, PaludoX0, Qeew, RaymondFam, Rolezn, Sathish9098, Tointer, a12jmx, arialblack14, ast3ros, ayeslick, bin2chen, btk, caventa, ch0bu, chaduke, chrisdior4, delfin454000, descharre, evan, fatherOfBlocks, georgits, gz627, jasonxiale, joestakey, kaden, lukris02, nicobevi, nogo, oberon, oyc_109, pfapostol, rbserver, sakshamguruji, seeu, shark, simon135, slvDev, synackrst, tnevler, whilom, zaskoh
253.3371 USDC - $253.34
Issue | Instances | |
---|---|---|
[NC-01] | Inconsistent Astaria LOGO Format | All Logo Contracts |
[NC-02] | NatSpec Comment Used For Logo | All Logo Contracts |
[NC-03] | Long Lines (> 120 Characters) | 40 |
[NC-04] | Contracts Missing @title NatSpec Tag | 30 |
[NC-05] | Inconsistent Comment Initial Spacing | 21 |
[NC-06] | Inconsistent Named Returns | 14 |
[NC-07] | Contract Layout Voids Solidity Docs | 13 |
[NC-08] | Order of Functions Not Compliant With Solidity Docs | 11 |
[NC-09] | Modifiers/Functions Should Replace Duplicate require Statements | 5 |
[NC-10] | No License Indication | 4 |
[NC-11] | acheive Spelling Mistake | 2 |
[NC-12] | nft vs NFT | 2 |
[NC-13] | Use of pragma experimental ABIEncoderV2; | 2 |
[NC-14] | Inconsistent Code Style For Similar Code | 2 |
[NC-15] | Extra Space in Comment | 1 |
[NC-16] | Power of Ten Literal > 10e3 Not In Scientific Notation | 1 |
[NC-17] | Duplicate Code | 1 |
[NC-18] | Empty Comment | 1 |
[NC-19] | require Matches Existing Modifier | 1 |
[L-01] | Lack of events | All Contracts |
Some contracts start a newline with a space in the Astaria Logo at the top of the contract, some do not. Consider sticking to a single style.
Style A
: L3-L12
Style B
: L3-L12
Style A
Contracts: BeaconProxy.sol, ClearingHouse.sol, CollateralToken.sol, PublicVault.sol, AstariaRouter.sol, AstariaVaultBase.sol, IBeacon.sol, ISecurityHook.sol, IRouterBase.sol, ITransferProxy.sol, IFlashAction.sol, IStrategyValidator.sol, IAstariaVaultBase.sol, IWithdrawProxy.sol, IVaultImplementation.sol, IPublicVault.sol, ICollateralToken.sol, IAstariaRouter.sol, ILienToken.sol.
Style B
Contracts: TransferProxy.sol, Vault.sol, WithdrawProxy.sol, LienToken.sol, WithdrawVaultBase.sol, VaultImplementation.sol.
The Astaria Logo in all related contracts uses the NatSpec comment /**
instead of the standard /*
. /**
is losely reserved for NatSpec comments. Consider using the standard /*
comment for the Astaria Logo.
Lines with greater length than 120 characters are used. The Solidity Style Guide suggests that all lines should be 120 characters or less in width.
The following lines are longer than 120 characters, it is suggested to shorten these lines:
src/WithdrawProxy.sol
src/CollateralToken.sol
src/PublicVault.sol
src/AstariaRouter.sol
src/LienToken.sol
src/VaultImplementation.sol
src/interfaces/IERC1155Receiver.sol
src/interfaces/IWithdrawProxy.sol
lib/gpl/src/interfaces/IERC4626RouterBase.sol
lib/gpl/src/interfaces/IUniswapV3PoolState.sol
src/interfaces/IPublicVault.sol
src/interfaces/ICollateralToken.sol
src/interfaces/IAstariaRouter.sol
src/interfaces/ILienToken.sol
@title
NatSpec Tag30 out of 45 of the contracts in scope are missing a @title
tag. Given that 15 contracts all have a @title
tag, consider adding one per the 30 remaining contracts.
TransferProxy.sol, BeaconProxy.sol, ClearingHouse.sol, CollateralToken.sol, WithdrawVaultBase.sol, AstariaVaultBase.sol, ERC4626-Cloned.sol, ERC20-Cloned.sol, ERC721.sol, IBeacon.sol, IERC165.sol, ISecurityHook.sol, IRouterBase.sol, IERC20Metadata.sol, ITransferProxy.sol, IFlashAction.sol, IStrategyValidator.sol, IAstariaVaultBase.sol, IERC1155Receiver.sol, IERC20.sol, IWithdrawProxy.sol, IERC721.sol, IERC1155.sol, IERC4626.sol, IVaultImplementation.sol, IPublicVault.sol, IV3PositionManager.sol, ICollateralToken.sol, IAstariaRouter.sol, and ILienToken.sol are missing a @title
tag.
Some comments have an initial space after //
or ///
while others do not. It is best for code clearity to keep a consistent style.
// foo
): BeaconProxy.sol, WithdrawProxy.sol, WithdrawVaultBase.sol, ERC4626Router.sol, ERC4626RouterBase.sol, ERC4626-Cloned.sol, ERC20-Cloned.sol, ERC721.sol, IBeacon.sol, IERC165.sol, ISecurityHook.sol, IMulticall.sol, IRouterBase.sol, IERC721Receiver.sol, ITransferProxy.sol, IFlashAction.sol, IStrategyValidator.sol, IAstariaVaultBase.sol, IERC1155Receiver.sol, IERC20.sol, IERC4626Router.sol, IWithdrawProxy.sol, IUniswapV3Factory.sol, IERC4626RouterBase.sol, IERC1155.sol, IUniswapV3PoolState.sol, IERC4626.sol, IVaultImplementation.sol, and IPublicVault.sol.//foo
).Some functions use named returns and others do not. It is best for code clearity to keep a consistent style.
returns(uint256 foo)
): ERC4626Router.sol, and ERC4626RouterBase.sol.returns(uint256)
): BeaconProxy.sol, Vault.sol, WithdrawVaultBase.sol, and AstariaVaultBase.sol.The Solidity Style Guide suggests the following contract layout order: Type declarations, State variables, Events, Modifiers, Functions.
The following contracts are not compliant (examples are only to prove the layout are out of order NOT a full description):
The Solidity Style Guide suggests the following function order: constructor, receive function (if exists), fallback function (if exists), external, public, internal, private.
The following contracts are not compliant (examples are only to prove the functions are out of order NOT a full description):
require
StatementsThere are times in the codebase where the same or similar require statements are used and can be made into a single modifier for better code cleanliness.
/src/ClearingHouse.sol Links: 199, 216.
199: require(msg.sender == address(ASTARIA_ROUTER.COLLATERAL_TOKEN())); 216: require(msg.sender == address(ASTARIA_ROUTER.COLLATERAL_TOKEN()));
lib/gpl/src/ERC4626-Cloned.sol Links: 27, 43.
27: require(shares > minDepositAmount(), "VALUE_TOO_SMALL"); 43: require(assets > minDepositAmount(), "VALUE_TOO_SMALL");
lib/gpl/src/ERC721.sol Links: 155-160, 171-176.
155: require( 156: to.code.length == 0 || 157: ... 171: require( 172: to.code.length == 0 || 173: ...
240: require( 241: to.code.length == 0 || 242: ... 260: require( 261: to.code.length == 0 || 262: ...
/src/VaultImplementation.sol Links: 78, 96, 105, 144, 147, 211.
78: require(msg.sender == owner()); //owner is "strategist" 96: require(msg.sender == owner()); //owner is "strategist" 105: require(msg.sender == owner()); //owner is "strategist" 144: require(msg.sender == owner()); //owner is "strategist" 147: require(msg.sender == owner()); //owner is "strategist" 211: require(msg.sender == owner()); //owner is "strategist"
Some contracts are missing a license indication. If no license is used SPDX-License-Identifier: UNLICENSED
should be at the top of a contract.
The following contracts are missing a license:
acheive
Spelling MistakeThe word achieve
is misspelled as acheive
.
/src/interfaces/IV3PositionManager.sol Links: 122, 123.
122: /// @return amount0 The amount of token0 to acheive resulting liquidity 123: /// @return amount1 The amount of token1 to acheive resulting liquidity
nft
vs NFT
NFT
is spelled in all capitals everywhere in the codebase except 2 places (EX. L27). Consider changing all occurrences of nft
to NFT
.
/src/ClearingHouse.sol Links: 115.
115: address tokenContract, // collateral token sending the fake nft
/src/interfaces/ICollateralToken.sol Links: 63.
63: //mapping of a security token hook for an nft's token contract address
pragma experimental ABIEncoderV2;
As of Solidity v0.8.0 "pragma experimental ABIEncoderV2;
is still valid, but is deprecated and has no effect". Consider removing all occurrences of pragma experimental ABIEncoderV2;
when contracts are >=v0.8.0.
/src/CollateralToken.sol Links: 16.
16: pragma experimental ABIEncoderV2;
/src/LienToken.sol Links: 16.
16: pragma experimental ABIEncoderV2;
There are two sections of code that are very similar yet written in 2 different formats A
and B
seen below:
lib/gpl/src/ERC721.sol
A
: 155-160, 171-176.
require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" );
require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received( msg.sender, address(0), id, "" ) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" );
Style B
will not exceed 120 characters if expanded like A
. Consider maintaining the same code style for better readability.
There is an extra space in the comment L25 of TransferProxy.sol between is
and Auth
.
/src/TransferProxy.sol Links: 25.
25: //only constructor we care about is Auth
10e3
Not In Scientific NotationPower of ten literals > 10e3
are easier to read when expressed in scientific notation. Consider expressing large powers of ten in scientific notation (Ex. 10e5).
/src/PublicVault.sol Links: 604.
604: uint256 fee = x.mulDivDown(VAULT_FEE(), 10000);
Duplicate code should be put into a function. L239-L242 and L257-L260 of PublicVault.sol have the same code.
There is an empty comment before the second //
. Consider removing the first empty comment on L831 of LienToken.sol.
/src/LienToken.sol Links: 831.
831: // // slope does not need to be updated if paying off the rest, since we neutralize slope in beforePayment()
require
Matches Existing ModifierIn the mint
function of WithdrawProxy.sol, require(msg.sender == VAULT(), "only vault can mint");
is used which already matches the existing modifier onlyVault
. Consider re-using onlyVault
instead of inlining the modifier.
events
There is a general lack of events
in the codebase especially in critical functions like a privileged actor role change (EX. 339).
#0 - c4-judge
2023-01-26T14:07:54Z
Picodes marked the issue as grade-a
🌟 Selected for report: c3phas
Also found by: 0x1f8b, 0xAgro, 0xSmartContract, 0xackermann, 0xkato, Aymen0909, Bnke0x0, CloudX, IllIllI, PaludoX0, Rageur, Rahoz, RaymondFam, ReyAdmirado, Rolezn, SadBase, SaeedAlipoor01988, caventa, chaduke, chrisdior4, fatherOfBlocks, fs0c, kaden, nogo, pfapostol, shark, synackrst
36.79 USDC - $36.79
Issue | Instances | Gas Savings (from provided tests) | |
---|---|---|---|
[G-01] | && In If Statement(s) | 4 | -5142 |
[G-02] | i-- /i-=1 Used Over --i | 3 | -243 |
[G-03] | x += y Used | 13 | -90 |
[G-04] | && In Require Statement(s) | 1 | -72 |
[G-05] | Struct Can Be Packed Into Fewer Slots | 1 |
Seperating if statements saves gas. Example
//From if (a != HIGH && b != LOW) { //Do Something } //To if (a != HIGH) { if (b != LOW) { //Do Something } }
/src/PublicVault.sol Links: 586.
586: if (v.depositCap != 0 && totalAssets() >= v.depositCap) {
/src/AstariaRouter.sol Links: 476.
476: if (timeToSecondEpochEnd > 0 && details.duration > timeToSecondEpochEnd) {
/src/LienToken.sol Links: 268, 271.
268: if (stateHash == bytes32(0) && stack.length != 0) { 271: if (stateHash != bytes32(0) && keccak256(abi.encode(stack)) != stateHash) {
i--
/i-=1
Used Over --i
--i
increments i
directly. When using i--
/i-=1
solc must create a temporary value, thus resulting in higher gas.
/src/PublicVault.sol Links: 539.
539: s.epochData[epoch].liensOpenForEpoch--;
Suggested Change
539: --s.epochData[epoch].liensOpenForEpoch;
/lib/gpl/src/ERC721.sol Links: 136, 223.
136: s._balanceOf[from]--; 223: s._balanceOf[owner]--;
Suggested Change
136: --s._balanceOf[from]; 223: --s._balanceOf[owner];
x += y
Usedx = x + y
is cheaper than x += y
in some cases like below.
/src/WithdrawProxy.sol Links: 237, 313.
237: s.withdrawReserveReceived += amount; 313: s.expected += newLienExpectedValue.safeCastTo88();
/src/PublicVault.sol Links: 565, 583, 606, 624.
565: s.slope += computedSlope.safeCastTo48(); 583: s.yIntercept += assets.safeCastTo88(); 606: s.strategistUnclaimedShares += feeInShares; 624: s.yIntercept += params.increaseYIntercept.safeCastTo88();
/src/AstariaRouter.sol Links: 512.
512: totalBorrowed += payout;
/src/LienToken.sol Links: 160, 210, 480, 524, 720, 735.
160: potentialDebt += _getOwed( 210: maxPotentialDebt += _getOwed(newStack[i], newStack[i].point.end); 480: potentialDebt += _getOwed(newStack[j], newStack[j].point.end); 524: totalSpent += spent; 720: maxPotentialDebt += _getOwed(stack[i], stack[i].point.end); 735: maxPotentialDebt += _getOwed(stack[i], end);
Seperating require statements saves gas. Example
//From require(a != HIGH && b != LOW); //To require(a != HIGH); require(b != LOW);
/src/Vault.sol Links: 65.
65: require(s.allowList[msg.sender] && receiver == owner());
Structs are divided into 32 slots. Minimizing the number of slots used per struct saves on gas.
src/interfaces/IAstariaRouter.sol
StrategyDetailsParam
struct uses 3 32 byte slots: [[1], [32], [20]] respectively. The slots can be rearranged as follows to minimize the number of slots to 2: [[1, 20], [32]].#0 - c4-judge
2023-01-25T23:26:11Z
Picodes marked the issue as grade-b