Omniscia Avocado Fund Audit
AvocadoVault Manual Review Findings
AvocadoVault Manual Review Findings
AVT-01M: Incorrect Cap Assumption
| Type | Severity | Location |
|---|---|---|
| Logical Fault | ![]() | AvocadoVault.sol:L125, L211-L215 |
Description:
The AvocadoVault::_deposit function assumes that the cap will always be larger than the current amount of assets which may not be correct due to re-configurations of the cap or a direct token transfer.
Impact:
Deposits that should normally fail due to the VaultCapExceeded error would yield an undefined underflow error in the current implementation due to an incorrect assumption.
Example:
122function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal override whenNotPaused {123 uint256 cap = vaultCap;124 uint256 current = totalAssets();125 if (current + assets > cap) revert VaultCapExceeded(assets, cap - current);126 super._deposit(caller, receiver, assets, shares);127}Recommendation:
We advise the code to use a saturating subtraction, yielding 0 if the cap is lower than the current asset balance.
Alleviation (a859cd2191d509cbc6d47508bdd44ec6d3cc9844):
The code was updated to properly perform a saturating subtraction and thus yield a correct VaultCapExceeded error regardless of what the cap is presently configured as.
AVT-02M: Improper Performance Fee Adjustment
| Type | Severity | Location |
|---|---|---|
| Logical Fault | ![]() | AvocadoVault.sol:L225-L229 |
Description:
The AvocadoVault::setPerformanceFee function permits a retro-active application of a new performanceFeeBps configuration on already-gathered interest which is incorrect.
Impact:
A performanceFeeBps adjustment can retroactively apply to a large amount of interest that has already gathered which is unfair to depositors who supplied funds at different configurations.
Example:
224/// @inheritdoc IAvocadoVault225function setPerformanceFee(uint16 newFeeBps) external onlyOwner {226 if (newFeeBps > MAX_FEE_BPS) revert InvalidFee(newFeeBps);227 emit PerformanceFeeUpdated(performanceFeeBps, newFeeBps);228 performanceFeeBps = newFeeBps;229}Recommendation:
We advise the function to invoke the AvocadoVault::collectFees function before the performance fee BPS is reconfigured, ensuring that performance fee BPS adjustments are not retroactive.
Alleviation (a859cd2191d509cbc6d47508bdd44ec6d3cc9844):
The AvocadoVault::setPerformanceFee function was updated to properly collect any pending interest prior to updating the performanceFeeBps value, addressing this exhibit.
AVT-03M: Inexistent Access Control of Fee Collection
| Type | Severity | Location |
|---|---|---|
| Logical Fault | ![]() | AvocadoVault.sol:L184, L232-L235 |
Description:
The AvocadoVault::collectFees function does not have access control, permitting a user to collect fees right before the feeRecipient is changed as well as collecting fees at a time the feeRecipient might not be aware of them.
Impact:
Any adjustment of the feeRecipient is slightly unsafe as a transaction can be submitted before it claiming any pending interest to the wrong previously-configured account.
Example:
231/// @inheritdoc IAvocadoVault232function setFeeRecipient(address newRecipient) external onlyOwner {233 if (newRecipient == address(0)) revert ZeroAddress();234 feeRecipient = newRecipient;235}Recommendation:
We advise some form of access control to be imposed, either by requiring the caller to be the feeRecipient or the owner of the contract.
Alleviation (a859cd2191d509cbc6d47508bdd44ec6d3cc9844):
The AvocadoVault::collectFees function was updated to properly impose access control, ensuring that it can be solely invoked by the owner of the contract or the feeRecipient themselves.
AVT-04M: Incorrect Handling of Interest
| Type | Severity | Location |
|---|---|---|
| Mathematical Operations | ![]() | AvocadoVault.sol:L195, L198-L200, L203 |
Description:
The AvocadoVault::collectFees function incorrectly handles the performance fee on interest as it calculates shares based on the feeAmount that is part of the TVL of the vault at the given time whereas normal deposit flows are based on the TVL before the funds are pulled into the contract.
Impact:
All performance fee collections presently cause a dilution to occur, with the S-02 fix being incorrect as presently implemented.
Example:
181/// @notice Collect performance fees on accrued interest182/// @dev Mints shares to the fee recipient rather than transferring USDC183/// (avoids liquidity drain; fee recipient redeems at will)184function collectFees() external {185 uint256 interest = accruedInterest;186 if (interest == 0) return;187
188 uint256 feeAmount = (interest * performanceFeeBps) / BPS_DENOMINATOR;189 if (feeAmount == 0) return;190
191 accruedInterest = 0;192
193 // S-02 fix: compute feeShares BEFORE reducing deployedAssets194 // to prevent share dilution from lowered totalAssets()195 uint256 feeShares = convertToShares(feeAmount);196
197 // Reduce deployedAssets AFTER share conversion198 if (feeAmount <= deployedAssets) {199 deployedAssets -= feeAmount;200 }201
202 // Mint fee shares to recipient203 _mint(feeRecipient, feeShares);204
205 emit FeesCollected(feeRecipient, feeAmount);206}Recommendation:
We advise the code and flow to be revised, increasing the deployedAssets in AvocadoVault::reportInterest by the fee-free amount that remains from the interestAmount, instead storing the actual fee in the accruedInterest data entry.
This will permit the AvocadoVault::collectFees function to measure the accruedInterest as a new deposit, increasing the deployedAssets by the interestAmount after measuring the share amount required.
Alleviation (a859cd2191d509cbc6d47508bdd44ec6d3cc9844):
The overall interest accrual flow has been revised per our instructions, ensuring that the fee shares that are minted from fee-related interest are tracked properly and do not dilute existing holders.


