Platform: Code4rena
Start Date: 01/04/2024
Pot Size: $120,000 USDC
Total HM: 11
Participants: 55
Period: 21 days
Judge: Picodes
Total Solo HM: 6
Id: 354
League: ETH
Rank: 49/55
Findings: 1
Award: $32.96
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: DadeKuma
Also found by: 0xStalin, 0xhacksmithh, 99Crits, Aymen0909, Bauchibred, CodeWasp, Dup1337, IllIllI, John_Femi, K42, KupiaSec, Naresh, Rhaydden, Rolezn, Sathish9098, Topmark, ZanyBonzy, albahaca, bareli, blockchainbuttonmasher, cheatc0d3, codeslide, crc32, d3e4, favelanky, grearlake, hihen, jasonxiale, jesjupyter, lanrebayode77, lirezArAzAvi, lsaudit, mining_mario, oualidpro, pfapostol, radin100, rbserver, sammy, satoshispeedrunner, slvDev, twcctop, zabihullahazadzoi
32.9585 USDC - $32.96
maxDeposit()
directly in deposit()
functionFile: CollateralTracker.sol
function maxDeposit(address) external pure returns (uint256 maxAssets) { return type(uint104).max; }
function deposit(uint256 assets, address receiver) external returns (uint256 shares) { if (assets > type(uint104).max) revert Errors.DepositTooLarge();
Function maxDeposit()
hardcodes the maximum amount of the underlying asset that can be deposited into the Vault to type(uint104).max
.
The very same value is used in function deposit()
. To improve the code readability, it would be better to use maxDeposit()
directly in the deposit()
function:
function deposit(uint256 assets, address receiver) external returns (uint256 shares) { if (assets > maxDeposit(receiver)) revert Errors.DepositTooLarge();
This will stick to the convection of other EIP-4626 related functions. E.g., function redeem()
straightforwardly checks if shares > maxRedeem(owner)
; function withdraw()
straightforwardly checks if assets > maxWithdraw(owner)
:
function withdraw( uint256 assets, address receiver, address owner ) external returns (uint256 shares) { if (assets > maxWithdraw(owner)) revert Errors.ExceedsMaximumRedemption();
function redeem( uint256 shares, address receiver, address owner ) external returns (uint256 assets) { if (shares > maxRedeem(owner)) revert Errors.ExceedsMaximumRedemption();
This implies, that function deposit()
should also straightforwardly check if assets > maxDeposit(receiver)
balanceOfBatch()
breaks the composability with EIP-1155File: ERC1155Minimal.sol
According to provided documentation, ERC1155Minimal.sol
is A minimalist implementation of the ERC1155 token standard without metadata
.
According to EIP-1155: The balanceOfBatch function allows clients to retrieve balances of multiple owners and token IDs with a single call.
However, the current implementation is incorrect, because it does not provide any verification if owners.length == ids.length
.
owners.length > ids.length
Function will simply revert, because there will be no enough elements in ids
array:balances[i] = balanceOf[owners[i]][ids[i]];
E.g., calling balanceOfBatch([0xAAA, 0xBBB, 0xCCC], [1, 2])
will simply revert on accessing non-existing ids[3]
element.
owners.length < ids.length
Function will return incorrect results, because it will only iterate from 0 to owners.length
for (uint256 i = 0; i < owners.length; ++i)
Function will update balances
at indexes from 0 to owners.length
. When owners.length < ids.length
, the remaining ids
values will be omitted.
E.g., calling balanceOfBatch([0xAAA, 0xBBB], [1, 2, 3])
will omit 3
. Other protocols which integrates with ERC1155Minimal.sol
might be mislead by this behavior.
Please notice, that the protocol straightforwardly states that its implementation is a modified version from Solmate
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/v7/src/tokens/ERC1155.sol)
Examining Solmate's implementation shows the extra check:
require(owners.length == ids.length, "LENGTH_MISMATCH");
This check should also be implemented in ERC1155Minimal.sol
.
ERC1155Minimal.sol
misses batch minting and batch burningFile: ERC1155Minimal.sol
The protocol straightforwardly states that its implementation is a modified version from Solmate
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/v7/src/tokens/ERC1155.sol)
The Solmate's code implements two additional functions: _batchMint()
and _batchBurn()
which allow to perform batch minting and batch burnings. Those functions are, however missed in the ERC1155Minimal.sol
. While this does not pose any security risk - consider extending the ERC1155Minimal.sol
abilities of burning and minting in batches.
COMMISSION_FEE
File: CollateralTracker.sol
COMMISSION_FEE = _commissionFee;
Immutable variable COMMISSION_FEE
is set in the constructor, however, its value is nowhere check.
If, during the deployment, this variable would be set to value greater than DECIMALS
constant (> 10_000
) - some functions in CollateralTracker.sol
will overflow and the contract will be needed to be redeployed with proper COMMISSION_FEE
value.
function previewDeposit(uint256 assets) public view returns (uint256 shares) { // compute the MEV tax, which is equal to a single payment of the commissionRate on the FINAL (post mev-tax) assets paid unchecked { shares = Math.mulDiv( assets * (DECIMALS - COMMISSION_FEE),
function previewMint(uint256 shares) public view returns (uint256 assets) { [...] unchecked { assets = Math.mulDivRoundingUp( shares * DECIMALS, totalAssets(), totalSupply * (DECIMALS - COMMISSION_FEE)
When contract will be deployed with COMMISSION_FEE > DECIMALS
, DECIMALS - COMMISSION_FEE
in functions previewDeposit()
and previewMint()
will overflow (please notice that this calculation is in the unchecked
block) - leading to incorrect calculations.
File: CollateralTracker.sol
Better formatting the math operations in the comments sections increases the code readability, thus it's highly recommended.
E.g., in CollateralTracker.sol
at lines 457-460, math operations are properly formatted:
// finalAssets - convertedAssets = commissionRate * finalAssets // finalAssets - commissionRate * finalAssets = convertedAssets // finalAssets * (1 - commissionRate) = convertedAssets // finalAssets = convertedAssets / (1 - commissionRate)
However, line 198 misses the proper format:
// taylor expand log(1-sellCollateralRatio)/log(1.0001) around sellCollateralRatio=2000 up to 3rd order
Use whitespaces, to properly format math operations - so it will be easier to read them and understand them:
// taylor expand log(1 - sellCollateralRatio) / log(1.0001) around sellCollateralRatio = 2000 up to 3rd order
#0 - c4-judge
2024-04-26T17:30:49Z
Picodes marked the issue as grade-b