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: 62/103
Findings: 2
Award: $88.11
🌟 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
51.3151 USDC - $51.32
string.concat()
to concatenate stringsAs of Solidity v0.8.12 the string.concat(a,b)
function is the preferred method, due to it being much more readable than abi.encodePacked()
.
WithdrawProxy.sol
Line 110WithdrawProxy.sol
Line 124PublicVault.sol
Line 83PublicVault.sol
Line 93The pragma
pragma experimental ABIEncoderV2;
is still valid, but it is deprecated and has no effect. If you want to be explicit, please usepragma abicoder v2;
instead.
Source: docs.soliditylang.org/en/v0.8.13/080-breaking-changes.html#silent-changes-of-the-semantics:
safeTransferFrom()
doesn't check for contract existence.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
Source: SafeTransferLib.sol
Line 9
As a result, safeTransferFrom()
will return success even if the contract is non-existent. Due to this a contract may think that funds have transferred successfully when in reality no funds were transferred.
Here are some instances of this issue:
File: Vault.sol
Line 66, Line 72
66: ERC20(asset()).safeTransferFrom(msg.sender, address(this), amount); 72: ERC20(asset()).safeTransferFrom(address(this), msg.sender, amount);
File: TransferProxy.sol
Line 34
34: ERC20(token).safeTransferFrom(from, to, amount);
Using the delete
keyword more clearly states your intention.
For example:
File: WithdrawProxy.sol
Line 284
s.finalAuctionEnd = 0;
The instance above could be changed to:
delete s.finalAuctionEnd;
It is recommended to use the correct spelling of words. Not doing so can decrease readability.
File: Vault.sol
Line 90
/// @audit vautls --> vaults 90: //invalid action private vautls can only be the owner or strategist
File CollateralToken.sol
(Line 126, Line 507)
/// @audit dont -> don't 126: //revert auction params dont match /// @audit its -> (it's || it is) 507: //get total Debt and ensure its being sold for more than that
File: PublicVault.sol
(Line 307, Line 374)
/// @audit calcualtion -> calculation 307: // reset liquidationWithdrawRatio to prepare for re calcualtion @audit then -> than 374: // prevent transfer of more assets then are available
File: IERC1155Receiver.sol
Line 36
/// @audit "a multiple" -> "multiple" 36: * @dev Handles the receipt of a multiple ERC1155 token types. This function
safeCastTo32()
not being utilizedSafeCastLib.sol
is imported in AstariaRouter.sol
. However, it is not fully utilized.
For instance:
File: AstariaRouter.sol
Line 112
s.minInterestBPS = uint32((uint256(1e15) * 5) / (365 days));
Instead of the above, consider utilizing safeCastTo32()
:
s.minInterestBPS = ((uint256(1e15) * 5) / (365 days)).safeCastTo32();
uint256()
castingFile: AstariaRouter.sol
Line 690 - Line 691
52: struct Details { 54: uint256 rate; //rate per second 58: } 690: uint256 maxNewRate = uint256(stack[position].lien.details.rate) - 691: s.minInterestBPS;
As seen above, stack[position].lien.details.rate
is cast into a uint256
when it is already of type uint256 rate;
. However, s.minInterestBPS
, which is type uint32
(defined here) is not cast into a uint256
.
With that in mind, line 690-691 could be refactored to:
690: uint256 maxNewRate = stack[position].lien.details.rate - 691: uint256(s.minInterestBPS);
#0 - c4-judge
2023-01-26T14:49:32Z
Picodes marked the issue as grade-b
🌟 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
The following functions have almost the same functionality:
File: VaultImplementation.sol
Line 101-117
101: /** 102: * @notice disable the allowList for the vault 103: */ 104: function disableAllowList() external virtual { 105: require(msg.sender == owner()); //owner is "strategist" 106: _loadVISlot().allowListEnabled = false; 107: emit AllowListEnabled(false); 108: } 109: 110: /** 111: * @notice enable the allowList for the vault 112: */ 113: function enableAllowList() external virtual { 114: require(msg.sender == owner()); //owner is "strategist" 115: _loadVISlot().allowListEnabled = true; 116: emit AllowListEnabled(true); 117: }
To save gas, consider merging the functionalities into a new function, setAllowListEnabled()
:
/** * @notice enable/disable the allowList for the vault */ function setAllowListEnabled(bool isEnabled) external virtual { require(msg.sender == owner()); //owner is "strategist" _loadVISlot().allowListEnabled = isEnabled; emit AllowListEnabled(isEnabled); }
require()
statements that use && saves gasInstead of using the && operator in a single require statement to check multiple conditions, using multiple require statements with 1 condition per require statement will save 3 GAS per &&.
PublicVault.sol
Line 672-675PublicVault.sol
Line 687-690Vault.sol
Line 65File: PublicVault.sol
Line 251-265
function deposit(uint256 amount, address receiver) public override(ERC4626Cloned) whenNotPaused returns (uint256) { VIData storage s = _loadVISlot(); if (s.allowListEnabled) { require(s.allowList[receiver]); } uint256 assets = totalAssets(); return super.deposit(amount, receiver); }
As seen above, uint256 assets
is never used. As such, it may be removed to save gas.
Use the unchecked
keyword to avoid unnecessary arithmetic checks and save gas when an underflow/overflow will not happen.
The following code line could be moved into the unchecked
block as it will not overflow:
File: AstariaRouter.sol
Line 512
In the following functions, the parameters are never used:
x += y
costs more gas than x = x + y
(same for -=
)Using the addition operator instead of plus equals saves around 22 gas
There are 22 instances of this issue:
File: AstariaRouter.sol
(Line 512)
512: totalBorrowed += payout;
File: LienToken.sol
L160, L161, L162, L164, L210, L480, L524, L525, L679, L720, L735, L830
160: potentialDebt += _getOwed( 161: params.encumber.stack[j], 162: params.encumber.stack[j].point.end 164: ); 210: maxPotentialDebt += _getOwed(newStack[i], newStack[i].point.end); 480: potentialDebt += _getOwed(newStack[j], newStack[j].point.end); 524: totalSpent += spent; 525: payment -= spent; 679: totalCapitalAvailable -= spent; 720: maxPotentialDebt += _getOwed(stack[i], stack[i].point.end); 735: maxPotentialDebt += _getOwed(stack[i], end); 830: stack.point.amount -= amount.safeCastTo88();
File: PublicVault.sol
, (#L174, #L179, #L380, #L405, #L565, #L583, #L606, #L624)
174: es.balanceOf[owner] -= shares; 179: es.balanceOf[address(this)] += shares; 380: s.withdrawReserve -= withdrawBalance.safeCastTo88(); 405: s.withdrawReserve -= drainBalance.safeCastTo88(); 565; s.slope += computedSlope.safeCastTo48(); 583: s.yIntercept += assets.safeCastTo88(); 606: s.strategistUnclaimedShares += feeInShares; 624: s.yIntercept += params.increaseYIntercept.safeCastTo88();
Files: VaultImplementation.sol
Line 404
404: amount -= fee;
File: WithdrawProxy.sol
(Line 237, Line 277, Line 313)
237 s.withdrawReserveReceived += amount; 277 balance -= transferAmount; 313 s.expected += newLienExpectedValue.safeCastTo88();
#0 - c4-judge
2023-01-25T23:53:39Z
Picodes marked the issue as grade-b