Umami Finance engaged Guardian to review the security of its GMX V2 market index, GMI. From the 11th of December to the 29th of December, a team of 6 auditors reviewed the source code in scope.
- Published
- Review window
- December 11 to 29, 2024
- Language
- Solidity
- Chains
- Arbitrum
- Sector
- Yield and vaults
- 3 Critical
- 6 High
- 8 Medium
- 42 Low
- 0 Informational
Scope
Overview
Umami Finance engaged Guardian to review the security of its GMX V2 market index, GMI. From the 11th of December to the 29th of December, a team of 6 auditors reviewed the source code in scope.
Issues Detected Throughout the engagement 9 High/Critical issues were uncovered and promptly remediated by the Umami Finance team. Several issues impacted the fundamental behavior of the protocol, following their remediation Guardian believes the protocol to uphold the functionality described for the GMI product.
Security Recommendation Given the number of High and Critical issues detected, Guardian supports an independent security review of the protocol at a finalized frozen commit.
Findings 59
-
RH-1 Critical All Asset Vault Funds Can Be Stolen Through Callbacks Logical Error Resolved
Description
Proof of concept: PoC
The request that is currently being executed in
RequestHandler.executeRequest()is cleared at the end of the function. This presents a critical problem as users can execute a deposit/withdraw request with a callback to an arbitrary address that they pass by usingassetVault.depositWithCallback()orassetVault.redeemWithCallback().This callback will be executed before the request gets removed, leaving room for exploitation.
assetVault.cancelRequest()immediately cancels a request and returns the funds to the user.- Create a deposit/withdraw request with a callback to an arbitrary contract we control.
- The keeper picks up the request and executes it.
- We call
assetVault.cancelRequest()in theafterDepositExecution()/afterWithdrawalExecution()
callback to cancel the request and return the funds to us immediately. 4. We now have the same funds/vault shares as before the request but have also received the funds from the request.
The exploit described above puts all funds in the asset vaults at risk of being stolen.
Recommendation
Call
aggregateVault.clearRequest(key)before executing the callback.Resolution
Umami Team: The issue was resolved in commit 8227df2.
-
AV-1 Critical Request Can Be Cancelled For Other Asset Vault Validation Resolved
Description
Proof of concept: PoC
When a user cancels their request with the function
cancelRequest, it is verified that the user cancelling the request is indeed thesenderwho sent the request. Afterwards, the funds contained in the Asset Vault are sent to the user depending on the amount of the request.However, there is no validation done to ensure that a user who deposited/redeemed into one Asset Vault is not cancelling the created request on the other Vault.
For example, for illustrative purposes, consider the drastic scenario of a user creating a 1 ether deposit into the ETH Vault. The user can then trivially call
cancelRequeston the USDC Vault, and be refunded 1e18 USDC. This leads to an enormous loss of funds for the USDC Vault depositors which is extremely easy to perform.Recommendation
Use the
vaultattribute of theOCRequestto ensure that cancellation is only performed for the Asset Vault in which the deposit/redeem was intended.Resolution
Umami Team: The issue was resolved in commit ea5e0e7.
-
AV-2 Critical Stale LLO Prices DoS DoS Resolved
Description
Chainlink LLO prices are only updated during the opening and closing of rebalancing periods and the
RequestHandler.executeRequestfunction. Yet, LLO prices are used in the logic for users to initiate withdraws/deposits on theAssetVaultwhen previewing the deposit fee with thepreviewDepositFeefunction and previewing the withdrawal fee with thepreviewWithdrawalFeefunction.Stale LLO prices, which users cannot update themselves, will cause users's withdraw/deposit initiations to revert as the withdraw/deposit fee estimation code checks that the LLO prices are no more stale than 1 Arbitrum block.
Additionally, protocol operators should update the latest LLO prices before calling the
cycleorfulfulRequestsfunctions during rebalance as these functions rely on up-to-date LLO prices.Recommendation
Do not rely on LLO pricing for the user-initiated deposits and withdrawals. Instead in the
previewDepositFeeandpreviewWithdrawalFeefunctions passfalseas theuseLlovalue.Resolution
Umami Team: The issue was resolved in commit 54ada0e.
-
GVH-1 High DOS Rebalance Through Simple Transfer DoS Resolved
Description
Proof of concept: PoC
A Denial of Service (DoS) attack can occur on rebalances by forcing the Umami-calculated
estimateExecutionFeeto be less than GMX’sminExecutionFee.As only one token is being deposited on a rebalance, Umami calculates the
estimateExecutionFeeand enters an if statement, returning the smaller value compared to if it were to deposit two tokens.The issue arises when an attacker forcibly sends 1 wei of the opposite token to GMX. The other gas limit value is then used, which is greater than what Umami used to calculate
estimateExecutionFee.This leads to a revert in the
validateExecutionFeefunction. With this attack, it becomes impossible to perform a rebalance, rendering the core feature of the protocol unusable.Recommendation
Send excess WETH for the execution fee, as any excess would be refunded anyway. This mitigates the risk of the DoS attack and ensures the rebalance functionality remains functional.
Resolution
Umami Team: The issue was resolved in commit 8f3f436.
-
GMIU-1 High Entire Misallocation Covered On Deposit Or Withdrawal Logical Error Resolved
Description
Proof of concept: PoC
In the
adjustToBalancefunction when theshareValueis insufficient to cover the entireunderAllocation, thedifferencearray with the entire positiveunderAllocationsis returned. However, theshareValueis insufficient to cover theseunderAllocationamounts.As a result, whenever a share amount is minted that is unable to cover the entire
underAllocation, the entireunderAllocationamounts will be charged to the caller while only remunerating the caller with the insufficient share amount that was specified.This issue is most clearly demonstrated with a mint of a single wei. The single wei will be insufficient to cover the entire
underAllocation, as a result, the caller is errantly required to provide the entireunderAllocationamount in GM tokens to mint the specified single wei of GMI.Similarly, this issue is present with withdrawals, where redeeming a single wei of shares will result in the withdrawer receiving the entire over-allocation amount. This is a fundamental accounting error and will significantly affect the
assetVaultshare values and the GMI valuation over time.Recommendation
Replace the return statement on line 72 with: Solarray.arrayAddProportion(toBalanceAmount, shareValue, difference, underAllocation, true);
Resolution
Umami Team: The issue was resolved in commit ea5e0e7.
-
AGV-1 High Mishandling Of Gas Stipends Logical Error Resolved
Description
Proof of concept: PoC
The protocol charges users an amount for gas since the protocol employs an asynchronous model where keepers pick the transactions up and execute them. The protocol checks whether the user has sent enough ETH in
msg.valueand if so adds their transaction to the uncompleted transaction queue.The issue here comes in due to how those gas fees are handled. The following checks whether the user has sent enough funds to cover the gas to be expended by the keeper:
require(msg.value >= gas, "AggregateVault: !gasRequirement");The issue with the above check is that it assumes that
gasis in terms of ETH instead of in gas units as it is.Given that the gas stipend for a request is within the 100,000 - 1,000,000 range the transaction's gas cost on the the user's side will be extremely low - less than a billionth of a cent since ETH is in 18 decimals. This will cause the protocol to lose funds in keeper gas fees on every deposit/withdrawal request that gets executed.
Recommendation
Consider converting the gas units into a notional value before checking whether the amount passed by the user is sufficient by multiplying it by
tx.gasprice.Resolution
Umami Team: The issue was resolved in commit 8227df2.
-
RH-2 High Wrong Withdrawal Fee Calculation Parameters Passed Logical Error Resolved
Description
Proof of concept: PoC
The protocol charges users fees on deposit and withdrawal. Those fees are based on the percentages set by the protocol and on the size in the asset vault's native token of the amount being deposited/withdrawn.
The issue here is due to a share size being passed to the
aggregateVault.previewWithdrawalFeefunction even though the function that calculates the said fee -VaultFees.getWithdrawalFee()assumes it is a native token amount.As the vault's TVL grows and more yield is gained through its strategies, each share will be worth more. However, this will not be represented when calculating the withdrawal fee as the calculations will think that the amount of shares passed in is the native token amount.
This directly impacts the protocol as the fees it will receive on withdrawal will be substantially lower than expected leading to a loss of fees for the protocol.
Recommendation
To mitigate the issue convert the vault shares into their native asset’s worth before passing them to
aggregateVault.previewWithdrawalFee().Resolution
Umami Team: The issue was resolved in commit ea5e0e7.
-
VF-1 High Wrong GMI Conversion Calculation Logical Error Resolved
Description
GMX fees get added on top of the base withdraw/deposit fees if they are enabled through
shouldUseGmxFee. In the case of a withdrawal, those fees are calculated based on the withdrawal size in GMI.The issue arises due to the following line, which gets used to turn the withdrawal size into GMI, which then gets turned into corresponding GM token amounts: gmi.sharesToMarketTokens(size * gmi.pps(prices) / 10 ** ERC20(asset).decimals(), prices).
The formula used for the calculation does not convert a USD notional amount into GMI, but quite the opposite.
This will always lead to a much larger fee due to the skewed GM token amounts, thus losing users' funds through excessive fees.
Recommendation
Convert
sizeinto a USD notional value and use a formula for converting USD into GMI:size * 1e18 /gmi.pps(prices).Resolution
Umami Team: The issue was resolved in commit ea5e0e7.
-
LCY-1 High Incorrect GMI Attribution Logical Error Resolved
Description
Proof of concept: PoC
The global state variable
vaultGmiAttributionrepresents the exact amount of GMI attributed to each vault. Throughout the codebase it is repeatedly set using the_commitGmiDeltaProportionsfunction from theLibCyclelibrary.This function incorrectly uses the
vaultGmiAttributionvalues as percentages, which they are not, instead of absolute values. Because of this, theprevAmountsare extremely large and any_amtbeing added to will be minuscule in comparison which leads to almost 0% change in the proportions. The larger the amount is, the more the allocations will deviate from the intended values.A direct, severe issue, appears during a rebalance when internally swapping GMI for native assets for internally settable differences between the 2 vaults. After swapping native tokens (L146-L152) the new allocations need to be saved from each vault, removing GMI from one vault and adding to another (L156-L172).
Since the call to
_commitGmiDeltaProportionsresults in no practical change in the percentage, users are directly losing funds via depreciation of vault shares, since ETH will be swapped in these cases but GMI attribution has not changed.To illustrate the impact, consider the following scenario:
- Total GMI valuation of $6 million
- Initial vault GMI allocation: 57% (57.0000090250015061%) USDC vault and 43% (42.9999909749984939%) WETH vault an amount approx $300K GMI (5% of GMI amount) is needed be moved from one vault to another
In this case, the current, incorrect implementation shows that the vault allocation, after adding the new amount, is: USDC: 57% (57.0000095000015852%), ETH: 43% (42.9999904999984147%). The new amount impact is erased and $300K worth of ETH is not GMI attributed.
If the correct implementation would be used, the resulting allocations are USDC: 60% (60.0000095000015853%), ETH: 40% (39.9999904999984146%). The error in this case is an absolute 3% value in allocation.
Recommendation
Modify the
_commitGmiDeltaProportionsto correctly work with and save the values as absolute amounts.Resolution
Umami Team: The issue was resolved in commits 90b2627 and e3e3cef.
-
RH-3 Medium Wrong Gas Stipend Passed To Callback Logical Error Resolved
Description
The storage of
AggregateVaulthas two variables for the two different gas fees that are paid by users when they create a request:executionGasAmountandexecutionGasAmountCallback. The former is for normal requests and the latter is for requests with callbacks.The issue arises due to how
executionGasAmountCallbackis handled when calling the callback address the user provided.executionGasAmountCallbackis passed directly as a gas stipend to the callback call even though it is intended to cover the whole call.This issue causes the protocol, and more specifically the keeper, to provide a much higher gas stipend to the callback, resulting in loss of funds on every transaction. Another potential issue is the depletion of the keeper's ETH balance through large amounts of malicious requests aimed at disrupting deposits and withdrawals of innocent users.
Recommendation
Consider passing
executionGasAmountCallback-executionGasAmountas a gas stipend to callback calls.Resolution
Umami Team: The issue was resolved in commit 8227df2.
-
VF-2 Medium Rebalance Fees Errantly Account For Withdrawals Logical Error Resolved
Description
When the rebalance fees are computed with the
_getVaultRebalanceFeesfunction, the magnitude of the funds withdrawn during the epoch is added to thelockedBalanceSansUserDeltawith theuserPositionDeltavariable. The resultinglockedBalanceSansUserDeltais ultimately the amount that is fee’d.However this incorrectly fees the remaining assets in the vault, the issue becomes clear considering the following (unreasonable, yet demonstrative) example:
- 90% of the funds in the vault are withdrawn in a single epoch
- 10% of the funds remain, and the users holding that remaining amount are subject to a fee based upon the entire 100%.
- Those who withdrew are not subject to this fee.
- The remaining users are exposed to an exorbitant fee as a percentage of their holdings.
This specific example is hyperbolic and unlikely to ever arise but is used merely to demonstrate the inaccuracy of the fee logic and the smaller-scale inequality that will occur on every rebalance.
Additionally, the current fee calculations clearly misaccount these withdrawn amounts because they are treated as if they were in the system for the entire epoch. The
performanceFeePercent,managementFeePercent, andtimelockYieldPercentare all computed based on thepercentYearof the past epoch and applied to these withdrawn amounts.However, the withdrawn amounts by definition cannot have been present in the vault for this entire period, in the worst case they will have been withdrawn from the vault at the beginning of the epoch.
Recommendation
Do not fee the remaining vault amounts based on the withdrawn amounts during the epoch.
Resolution
Umami Team: The issue was resolved in commit ea5e0e7.
-
GMI-1 Medium GMI Allocations Incorrectly Handle Saturated Markets Logical Error Resolved
Description
When a rebalance is underway, the GMI shares to be minted are validated by the
_validateMintableAmountsfunction from theGMIcontract.The function incorrectly considers that the maximum allowed USD equivalent value to be deposited into the asset-specific GMX pool is via the backing token with the lowest availability, not the highest. This results in incorrect asset allocations for cases where the equivalent amount value cannot be deposited in the pool via the saturated backing token, but could have been deposited in the other one.
In
_validateMintableAmounts, the maximum ETH value in USD (mintableEth) and maximum USDC value in USD (mintableUsdc) that can be deposited into each GMX asset market per backing token are calculated. Out of these two amounts, the largest should be selected as exactly how much can be deposited into the specific GMX pool using only one operation.The issue is that the
maxMintablechooses the smaller, not the larger out of the 2 values.This results in an incorrect maximum allocation amount for that particular asset pool, lower than it can be deposited. Consider a situation where a GMX pool gets long saturated and the protocol does a rebalance towards the short token.The
_validateMintableAmountsfunction will incorrectly indicate that the maximum you can deposit into that saturated pool is almost nothing since it uses the lowest available amount from the saturated one for validation.This situation would result in depositing into the fallback pool, which will revert when also saturated. Ultimately, the protocol becomes imbalanced, risking the loss of user funds.
Recommendation
Base the
previewMintand_validateMintableAmountsfunctions maximum mint amount on the asset being used to mint, not always take the greater or the smaller one. This would eliminate any issue that may appear due to over or underestimating the maximum amount.Resolution
Umami Team: The issue was resolved in commit 3940624.
-
GMI-2 Medium Deposit Prevented By Double Counting GM Deposits Logical Error Resolved
Description
In the
depositfunction, thepreviewMintfunction is called with the shares that are to be minted to the caller for their deposited GM amounts.The
previewMintfunction contains the_validateMintableAmountsvalidation at the end of the function which accounts for the share value being deposited into the GMX V2 system and reverts if the additional deposit tokens would put the GM market over the deposit cap.However, during a deposit, these GM tokens have already been minted and there are no additional long or short tokens that will be deposited into the GM market.
Therefore, this validation erroneously accounts for long/short tokens being deposited when they will not be, and as a result, causes unnecessary reverts when these phantom long/short token amounts exceed the deposit cap in GMX V2, ultimately causing DoS attacks on deposits.
Recommendation
Do not perform the
_validateMintableAmountsvalidation when depositing already minted GM tokens into GMI.Resolution
Umami Team: The issue was resolved in commit ea5e0e7.
-
RH-4 Medium Lack Of Slippage On Deposits And Withdrawals Slippage Resolved
Description
Deposits and withdrawals to/from the asset vaults are a 2 step operation. The user initiates the action, thus creating a pending order and a keeper asynchronously executes that operation.
Although the execution keeper operates relatively fast, between 1-3 blocks since the initial request, there will still exist situations where an operation is initiated exactly before a rebalance is opened. During a rebalance, operations cannot be executed by the keeper, as such the user action will only be executed after the rebalance closes.
An issue is that users expect their deposit/redeem to result in the exact amounts indicated by the
previewDepositandpreviewRedeemfunctions at that time, but because of the price being recalculated again at the time the operation is executed, users may experience negative slippage and obtain fewer tokens.During an epoch, a meaningful difference may not appear due to fast keeper response, but for those transactions that ultimately do become pending during a rebalance, the price difference may be significant enough of a loss. Since these operations flow normally, this situation will occur.
Recommendation
Add a slippage parameter when users deposit/redeem into the vault which will be passed and used by the
RequestHandlerwhen invoked by the keepers. Since the vaults are not meant to be ERC4626 compliant, this alteration does not come with a negative impact on the protocol.Resolution
Umami Team: The issue was resolved in commit ea5e0e7.
-
LCY-2 Medium Zero Slippage Protection On Swaps Logical Error Resolved
Description
In the
rebalanceGmifunction, ifisOppositeDirectionis true, it attempts to swap one token for the other to rebalance before the minting and burning processes. This is achieved by swapping through UniswapV3.When the swap is executed, the
_minOutis set to zero. This means that regardless of how much slippage the swap incurs, the execution will continue. This poses a security risk, as attackers can perform a sandwich attack on this swap on UniswapV3 and steal funds if they have sufficient capital.The impact of this is that any attempt to rebalance while
isOppositeDirectionis true will lead to an excess loss of funds due to the lack of slippage protection.Recommendation
Set a
_minOutvalue so that swaps do not incur more slippage than expected.Resolution
Umami Team: The issue was resolved in commit 7aa7161.
-
AV-3 Medium Vault Cap Can Be Bypassed Logical Error Resolved
Description
Proof of concept: PoC
Before depositing funds into the protocol, the
depositfunctions perform a check to ensure the vault cap will not be exceeded:require(totalAssets() + assets <= previewVaultCap(), "AssetVault: over vault cap");However, the
totalAssets()function does not consider the funds that are still pending to be sent into theAggregateVault. As a result, User A can deposit funds that reach the cap but will not be included in the TVL.User B will then make another deposit, and since the current TVL has not been updated yet, their deposit to AssetVault will also go through. Once both requests are settled, the vault cap will be bypassed.
Recommendation
Validate the vault cap upon request execution so that it cannot be easily exceeded by a potentially significant amount.
Resolution
Umami Team: The issue was resolved in commit ea5e0e7.
Guardian Team: Because the balance of unlodged assets is now added to the vault cap validation in the AssetVault, the more assets that are waiting to be lodged the less likely it is that the deposited assets will fail the validation. Consider omitting
asset.balanceOf(address(this))from the AssetVault cap validation. -
RH-5 Medium Not Subtracting Full Size On Withdrawal Logical Error Resolved
Description
Proof of concept: PoC
epochDeltais used to track the amount of deposits/withdrawals during an epoch. It grows positive when more funds are being deposited than being withdrawn and vice versa.The issue arises due to how the protocol increments and decrements
epochDelta. The protocol increments the delta with the amount deposited after the fees get subtracted from it, thus only incrementing with the amount that entered theAggregateVault.aggregateVault.incrementEpochDelta(underlyingToken, assetsSansFees.toInt256())However, the same pattern is not followed in the withdrawal logic. Instead of decreasing the whole amount that leaves the vault onlysize - feesget subtracted.aggregateVault.incrementEpochDelta(underlyingToken, -(assetsSansFees.toInt256()))This will result in an imbalance where depositing the same amount has a higher impact on increasingepochDeltacompared to the mitigating effect of withdrawing, thereby exposing the protocol to fund loss due to reduced fees.This happens due to the subtraction of positive
epochDeltafrom the current TVL during withdrawal fee calculations.Recommendation
Decrement
epochDeltabyassetsinstead ofassetsSansFees.Resolution
Umami Team: The issue was resolved in commit 54ada0e.
-
RH-6 Low Deposit Failures Unexpectedly Refund The Account Logical Error Resolved
Description
When a deposit request is made on behalf of another account and fails execution, the native assets are sent to the
receiverinstead of thesenderof the request, who initiated the request and provided the funds.This may be unexpected behavior as the sender would expect to receive their funds back if the request was not successfully executed.
Recommendation
Consider sending the funds to the
senderon a deposit request fail.Resolution
Umami Team: The issue was resolved in commit ea5e0e7.
-
GMIU-2 Low adjustToBalance Unbalances Upon Withdrawal Logical Error Acknowledged
Description
In the
adjustToBalancefunction, when there is an overallocation of GM tokens during a deposit the overallocation is offset by depositing into all other GM tokens at a ratio between the weight of the over-allocated market and the other market. This is desired during deposits as the higher-weighted market should receive a greater amount to move closer to the desired weightings.However, during a deposit, the opposite occurs. A larger portion of GM tokens are withdrawn from the higher target weight market, ultimately moving the market balances further away from the desired weightings rather than closer.
For example, consider the following scenario: The desired weightings are 0.2, 0.3, 0.25, and 0.25 respectively. And the respective GM token balances are 25, 25, 20, and 30.
The third market is underallocated by 5 GM tokens so markets 0,1, and 3 will have an increased withdrawal amount relative to the proportion of their target weighting to the target weighting of market 2.
Market 0 will only be decreased by 4 GM tokens, while market 1 will be decreased by 6 GM tokens. However the desired weighting of market 1 is higher than that of market 0, therefore this rebalancing moves the GM distribution further away from the desired weighting.
Withdrawals will often move GMI away from the desired weighting of GM tokens resulting in the desired positions not being met. This directly undermines the protocol’s goal of maintaining a balanced index for GMI.
Recommendation
When the
adjustToBalancefunction is being used in a withdrawal context thebalanceWeightingsshould be calculated using aweights[i] / weights[j]ratio rather than theweights[j] / weights[i]ratio so that higher weighted GM markets are reduced less than lower weighted ones.Resolution
Umami Team: Acknowledged.
-
GMIU-3 Low GMI previewMint Rounds GM Amounts Down Rounding Resolved
Description
In the
previewMintfunction, thegmValueToMintreturned from theGmiUtils.adjustToBalancefunction is rounded down as theshareValuewhich is used to determine the GM token amounts required uses round-down division.Therefore the amount of GM tokens that the
aggregateVaultwill use to mint GMI is often less than the value of the GMI shares that theaggregateVaultreceives.This is not an issue when the
aggregateVaultholds all of the GMI shares, however in the future when other third-party actors may also hold GMI, then all other holders of GMI are diluted by deposits.Recommendation
Consider rounding the
shareValueup in theadjustToBalancefunction to avoid diluting other holders of GMI for the future where theaggregateVaultis not the only holder of GMI.Resolution
Umami Team: The issue was resolved in commit 54ada0e.
-
GMI-3 Low GMI Deposit Amount Rounded Down Rounding Resolved
Description
When depositing GMI, the computed PPS used to calculate the shares received is rounded down. However, in the future, GMI may be supported for other third-party users. In that case, it would be important to round the PPS up upon deposits.
This way, any third-party users would receive less share value than the amount of GM tokens they deposit, rather than more share value compared to the amount of GM tokens they deposit. The precision loss due to rounding the PPS down is trivial but may pose a risk in the future.
Recommendation
Consider rounding the PPS up when computing the amount of shares to mint upon depositing into GMI.
Resolution
Umami Team: The issue was resolved in commit 54ada0e.
-
VF-3 Low Leap Years Are Unaccounted For Leap Years Resolved
Description
In the
VaultFeescontract the YEAR constant is defined as being exactly 365 days in seconds, rather than 365.25 days to account for leap years.Recommendation
Consider changing the YEAR value to
31557600to account for leap years.Resolution
Umami Team: The issue was resolved in commit b919f11.
-
AGVH-1 Low getVaultPPS Rounds In Favor Of Deposits Rounding Resolved
Description
In the
getVaultPPSfunction, round-down division is used regardless of whether the function is being used to determine the shares a user receives upon a deposit or the assets a user receives on withdrawal.This behavior rounds in the favor of the user upon deposits, allowing the user to deposit at a lower PPS due to precision loss. Though the precision loss is minor and is unlikely to have an impact it could be leveraged in a more complex attack.
Recommendation
To avoid any potential manipulations as a result of this precision loss, consider rounding up when the
getVaultPPSfunction is being used to determine the amount of shares a user will receive for a deposit and rounding down when thegetVaultPPSfunction is being used to determine the amount of assets a user will receive from a withdrawal.Resolution
Umami Team: The issue was resolved in commit 54ada0e
Guardian Team: We recommend rounding against the user in the AssetVault’s
ppsfunction during a redeem operation by passingisDeposit = false. -
LCY-3 Low Rebalance Functions Accessible Outside Of Rebalance Periods Access Control Resolved
Description
Neither the
cyclefunction nor thefulfilRequestsfunction validate that the system is indeed within a rebalancing period when they are being called.These functions can only be triggered by a trusted party, however, they should be restricted to only rebalance periods as important validation and caching must occur before these functions are invoked.
Recommendation
Validate that the protocol is currently in a rebalancing period in the
cycleandfulfilRequestsfunctions.Resolution
Umami Team: The issue was resolved in commit b919f11.
-
AV-4 Low CallbackHandler not assigned in setPeripheral Superfluous Code Resolved
Description
The
setPeripheralfunction does not allow a value to be assigned for thePeripheral.CallbackHandler. Nor is thePeripheral.CallbackHandleris used anywhere in the codebase.Recommendation
Consider removing the redundant
Peripheral.CallbackHandlervalue from thePeripheralenum, otherwise implement the desired use case for thePeripheral.CallbackHandlervalue.Resolution
Umami Team: The issue was resolved in commit b919f11.
-
GMIU-4 Low Weight Cannot Be 0 Warning Acknowledged
Description
When distributing the overallocated difference, the algorithm divides the
jthweight by theithweight. If the ith weight is 0, a division by 0 revert will occur.Because
adjustToBalanceis called inpreviewMintwhich is used in function_increaseGMI, a rebalance could fail due to a reverted cycle operation.Recommendation
Ensure none of the weights are set to 0.
Resolution
Umami Team: Acknowledged.
-
AVH-2 Low assetVault Shares Collateral Risk Warning Acknowledged
Description
The
assetVaultshares are valued at the cachedvaultState.rebalancePPSduring the rebalance period, but as soon as the rebalance period is closed, the valuation of theassetVaultshares will experience a stepwise jump to the current valuation.Because of this stepwise jump, the
assetVaultshares should not be a candidate for collateral in any borrow/lending system, as there is a risk that a malicious user could deposit vault shares as collateral while they are valued at the cached price and ultimately have an insolvent position when theassetVaultshare price drops.Borrow/lending platforms introduce a safety threshold between liquidation and solvency to address this, however there is a small risk that the magnitude of the stepwise jump exceeds this safety threshold. Any occurrence of this should be rare as rebalances are not intended to be large.
Recommendation
This is simply a warning to anyone who would integrate with the Umami GMI system and potentially accept the
assetVaultshares as collateral.Resolution
Umami Team: Acknowledged.
-
RH-7 Low Lacking Event For setCallbackEnabled Events Resolved
Description
When the callback functionality is enabled by the
RequestHandlercontract, by calling thesetCallbackEnabledfunction, there is no event is emitted.Recommendation
Emit an event on the call to the
setCallbackEnabledfunction.Resolution
Umami Team: The issue was resolved in commit 54ada0e
-
GLOBAL-1 Low Unused Functions Superfluous Code Resolved
Description
The following functions are not used in the protocol:
_vaultGmiProportion_exactOutputSwap
Recommendation
Consider removing these functions.
Resolution
Umami Team: The issue was resolved in commit b919f11.
-
RH-8 Low Request Gas May Not Match The Gas Provided By The User Logical Error Acknowledged
Description
The gas amount forwarded to the
request.callbackcontract is always the currentexecutionGasAmountCallbackin storage:gas: storageViewer.getExecutionGasAmountCallback()This presents an issue as users are made to pay it on submitting the request:
uint256 gas = _gasRequirement(callback != address(0));In the case of a change of the
executionGasAmountCallbackamount, users will either get more gas than they paid for or will get less.Recommendation
Consider keeping the gas amount the user paid for as a value in the request struct and using it instead.
Resolution
Umami Team: Acknowledged.
-
AGV-2 Low Performance Fees Errantly Measured Logical Error Resolved
Description
In the
closeRebalancePeriodfunction, the performance fees for the previous epoch are collected after setting thevaultState.rebalanceOpentofalse. As a result, the cachedvaultState.rebalancePPSis not returned when using thegetVaultPPSfunction to determine whether the performance fees should be charged.This behavior errantly treats the current vaultPPS after the rebalance as if it applied for the entire period before the rebalance occurred.
This results in cases where the performance fee is missed due to the rebalance dipping the vaultPPS below the watermark.
This may occur as a result of swapping fees and/or fees from GMX. Additionally, the performance fee may be charged when instead it should not if the
vaultPPSis increased above the watermark after the rebalance, though this case is rarer.Recommendation
Close the rebalance period after fees are calculated so that the cached vaultPPS is used rather than the PPS resulting from the rebalance.
Resolution
Umami Team: Acknowledged.
-
LCY-4 Low Unnecessary vaultIdx Variable Optimization Resolved
Description
In the
rebalanceGmifunction thevaultIdxis unnecessarily fetched using thegetTokenToAssetVauldIndexfunction when the vault index is directly available as the index of the for-loop,i.Recommendation
Use the for-loop index to indicate the vault rather than fetching the
vaultIdxwith thegetTokenToAssetVaultIndexfunction.Resolution
Umami Team: The issue was resolved in commit b919f11.
-
AV-5 Low Request Creator May Not Cancel The Request Unexpected Behavior Resolved
Description
In the
redeemandredeemWithCallbackfunctions, the owner is assigned as therequest.senderwhen the request is created. Therequest.senderis used to validate the account that may cancel the request in the Umami system.However, in some cases, the owner may not be the creator of the request. The owner may approve a third-party actor to create requests on their behalf. In this case, it would be appropriate to allow this third-party creator to cancel the request as well, however, they are not able to do so.
Recommendation
Consider changing the
request.senderto themsg.senderin theredeemandredeemWithCallbackfunctions. Otherwise, consider allowing both the owner and the request creator to cancel the request.Resolution
Umami Team: The issue was resolved in commit b919f11.
Guardian Team: A user with an allowance can cancel the request with function
cancelRequest, or have the request execution fail, and end up with the original owner's shares. Clearly document this behavior. -
LCY-5 Low Superfluous assetToMintFrom Variable Optimization Resolved
Description
In the
_increaseGmifunction theassetToMintFromis assigned to the_assetparameter in every iteration of the for-loop, however, it is unnecessary to declare thisassetToMintFromvariable as it will always be the_assetvalue.Recommendation
Remove the
assetToMintFromvariable declaration and use the_assetparameter value directly.Resolution
Umami Team: The issue was resolved in commit b919f11.
-
AGV-3 Low Changing Fee Recipient Should Be Done Only After A Rebalance Improvement Acknowledged
Description
Rebalancing the vaults results in a fee being paid to the designated
feeRecipientat that time. This recipient, as it is, can be changed at any time by a configurator by calling thesetFeeRecipientfunction from theAggregateVaultcontract.Changing the fee at a random point represents a loss for the original fee recipient, as they have been the recipient since the last rebalance up until that point. Consider the situation during a normal operation period and right before opening a rebalance period.
Recommendation
When setting the new fee recipient, set a pending recipient, which is changed only when the rebalancing period is closed and the fee is paid. At that point, the fee is paid to the old recipient and the pending recipient becomes the actual fee recipient.
Resolution
Umami Team: Acknowledged.
-
GLOBAL-2 Low No Validation Against Trapped Fees Validation Acknowledged
Description
When configuring the
vaultFeeswith thesetVaultFeesfunction, there is no requirement that thedepositFeeEscrowandwithdrawalFeeEscroware configured to nonzero addresses if the deposit and withdrawal fees are configured to nonzero amounts.Therefore, these fee amounts can get stuck in the vault contracts rather than being returned to the user or sent to any fee receiver.
Recommendation
Consider adding validation such that a nonzero fee amount cannot be configured without assigning the appropriate fee receiver to a nonzero address.
Resolution
Umami Team: Acknowledged.
-
VF-4 Low Excessive GMX Withdrawal Fees Logical Error Acknowledged
Description
Proof of concept: PoC
GMX swap fees get calculated and subtracted from a user whenever they deposit/withdraw shares from an asset vault. GMX fees in
VaultFees.getWithdrawalFee()are calculated with the size's backing in GM tokens based on their respective weights.The PPS and TVL of a vault are calculated with the liquid reserves of the native token in the aggregate vault, the GMI attributed to that vault, and also the opened external hedging position.
The issue here arises due to
VaultFees:137assuming that the whole withdrawal size is backed in GMI tokens and calculating it accordingly. This makes users get charged a GMX fee on 100% of their withdrawal size instead of only the fraction that needs to be liquidated through GMX, thus making the users lose funds due to the excessive fees.Recommendation
Consider calculating the GM market token amounts based on a fraction of
sizethat corresponds to thecurrent fraction of GMI reserves / TVL.Resolution
Umami Team: This is intended. We charge the fee for the entire withdrawal as if it were coming from GMI.
-
PV-1 Low Pausing Or Unpausing Spams Identical Events Improvement Resolved
Description
When calling the functions
_pauseor_unpausefrom thePausableVaultcontract, for each function there will be between 1 and 3 identical events being emitted. This can cause confusion for any 3rd party integrator.- for the
_pausefunction: minimum 1Pausedevent and maximum 2 more from the_pauseDepositand_pauseWithdrawalfunction calls - for the
_unpausefunction: minimum 1Unpausedevent and maximum 2 more from the_unpauseDepositand_unpauseWithdrawalfunction calls
Recommendation
For both the
_pauseand_unpausefunctions, remove the default emitted event, and for each pausing/unpausing event, either add an argument to identify if it was a deposit or withdrawal that was paused/unpaused, or create 2 separate events e.g.PausedDeposits/PausedWithdrawals.Resolution
Umami Team: The issue was resolved in commit 54ada0e.
- for the
-
AGV-4 Low High Netting Threshold Can Block Rebalancing Validation Resolved
Description
The
nettedThresholdvariable can be set by the configurator by calling the functionsetThresholdsfrom theAggregateVaultvault to any value.Setting it to over the maximum BPS will result in blocking all rebalances that opt to validate netting due to an underflow operation in the
NettingMathlibrary at line 95.Recommendation
Add a limit check for the
_newNettedThresholdinput in thesetThresholdsfunction so that it does not surpass the maximum BPS.Resolution
Umami Team: The issue was resolved in commit 54ada0e.
-
RH-9 Low Unused Callback Gas Not Refunded To User Logical Error Acknowledged
Description
Users can perform a deposit/withdrawal request with a callback to an arbitrary contract upon the completion of the request processing.
The issue arises because users are required to incur costs for the maximum gas amount they can utilize in the callback, even if their callback necessitates only a fraction of that for execution.
However, the unutilized gas in this process is not refunded, ultimately leading users to lose the value of any leftover gas in the callback.
Recommendation
Consider refunding the value of the gas units left at the gas price during the creation of the request.
Resolution
Umami Team: This is by design as the the keeper also has rebalance cost.
-
AGV-5 Low Epoch Delta Cleared Before Fees Are Calculated Logical Error Resolved
Description
The rebalancing fee is calculated based on the total TVL that was managed at the start of the current epoch. This fee is deducted when closing an epoch and, by calling the
closeRebalancePeriodfunction from theAggregateVaultcontract.The fee is incorrectly calculated because, in the
closeRebalancePeriodfunction call, the epoch delta is cleared by a_resetEpochDeltasfunction call on line 221, before the actual fee collection is done on line 237. This results in the overall fee being calculated on the ending epoch TVL instead of the opening TVL.A higher fee will be deducted if the epoch has more deposits and a lower fee will be deducted if the epoch has more withdrawals.
The end user is unaware of the fee value until the end of the epoch which, depending on the value, might have determined whether or not the user participated in the protocol during that epoch. Fee predictability is needed for users of the protocol.
Recommendation
Reset the epoch delta after the fees have been collected.
Resolution
Umami Team: The issue was resolved in commit 8227df2.
-
AGV-6 Low Stale Vault Index Allocation Used Logical Error Acknowledged
Description
When closing the rebalance period, a validation is done so that the current vault holding ratio is within the accepted upper and lower thresholds.
This validation is incorrect because it uses the index allocations that were saved when the rebalancing period was opened, which are stale, instead of using the current index allocations determined at the moment the rebalance is closing, and that are saved in the
vaultIndexAllocationvariable.Because of this, the vault ratio is always slightly incorrect since it calculates the vault index exposure as a factor of the current vault holdings (
vaultCumulativeHoldings) but compares it to the vault holdings at the time when rebalancing was opened (vaultHoldings) in the functionvaultDeltaAdjustmentfrom theNettingMathlibrary.This slight difference may lead to the vault ratio exceeding the threshold and resulting in an execution revert. Or passing a ratio when it would exceed the threshold normally.
Recommendation
When closing the rebalance period, always use the current index allocation, not the stale one saved when the rebalance period was opened.
Resolution
Umami Team: We only care about the total GMI in the vaults when validating the netting check.
-
AV-6 Low ETH Not Returned On Request Cancelation Logical Error Acknowledged
Description
When a user creates a request to either withdraw or deposit into Umami, they need to supply enough ETH to cover the execution fee.
This is so that the Umami keepers have funds to execute the transaction. Users who made the deposit or withdrawal can also cancel their request if it has not been executed yet.
The issue is that the ETH that the user supplied is not returned to the user. This is inconsistent behavior as the user loses any ETH that they sent for a request that never got executed.
Recommendation
Consider returning the ETH to the user since the keeper never used it.
Resolution
Umami Team: We chose to keep the gas here.
-
AGV-7 Low AggregateVault Can Be Completely Drained By Excessive Fees Centralized Risk Acknowledged
Description
When the vault fees are set by calling the function
setVaultFeesfrom theAggregateVaultcontract, there are no checks that do not surpass the equivalent of 100%.A mistake by the configurator or a compromised configurator can set the fees to such a value that they equate the entire vault assets. When a rebalancing happens, the vault assets will be sent to the fee recipient, which can also be set by the configurator via
setFeeRecipient.Recommendation
Add limitations so that fees cannot surpass 100% but should also include a lower, maximum allowed threshold.
Resolution
Umami Team: Acknowledged.
-
AGV-8 Low Attacker can Prevent Closing of Rebalance Warning Acknowledged
Description
For the
closeRebalancePeriodfunction to successfully execute, it must pass the check in thecheckNettingConstraintfunction.If the vault receives too little or too many GM tokens on a mint, it will cause the
vaultIndexAllocationto change and push thevaultRatiooutside of its bounds.An attacker can take advantage of this by force-sending some ETH or USDC to the GMX
depositVaultwhile Umami is minting. This will cause Umami to receive more GM tokens than expected, which will increase thevaultIndexAllocationand consequently thevaultRatio.If the vault ratio is already near the upper bound, it would only take a small amount to push the
vaultRatiobeyond the upper bound.If the
closeRebalancePeriodfunction cannot successfully execute, the protocol will remain in a rebalance state for longer than intended.Recommendation
This manipulation would be capital-intensive for an attacker and can be avoided by disabling the netting validation. However, it would be prudent to be aware of this risk when setting targets that would put the
vaultRationear the upper limit.Resolution
Umami Team: Acknowledged.
-
AGV-9 Low Changing Fee Percentage Should Be Done After Rebalance Logical Error Acknowledged
Description
Rebalancing the vaults results in a fee being paid in assets taken from the vaults themselves. Settings of the rebalance fee percentages can be directly changed by calling the
setVaultFeesfunction from theAggregateVaultcontract.This is an issue, as changing the fee settings during an epoch alters the perceived risk that users associate when interacting with the vaults, high taxes would also make the vaults less appealing and in return would result in a lower utilization.
The
epochDeltacomponent as well as fee watermark PPS can be changed at any time by calling thesetAssetVaultsfunction from theAggregateVaultcontract. Again this is an issue as it changes the fee during an epoch.Recommendation
When calling
setVaultFees, have the new fee percentages be set in a pending state. When a rebalancing is executed, after fees are deduced using the old fee values, then change them to the pending ones.Consider creating a separate function to update the fee watermark PPS value and date in the same manner as described above.
The function
setAssetVaultsshould only be called on severe vault changes, consider limiting its use as much as possible.Resolution
Umami Team: Acknowledged.
-
AGV-10 Low CLOSE_REBALANCE_HOOK Is Called Before Rebalance Gets Closed Logical Error Acknowledged
Description
The protocol has two different types of hooks:
- Hooks calling arbitrary addresses passed by users after a request of theirs gets executed/canceled.
- Protocol hooks that get evoked when a request gets queued or a rebalance gets opened/closed.
CLOSE_REBALANCE_HOOKis called before deposits are unpaused, which may restrict the callback from certain operations and limit its potential behavior.Recommendation
Consider calling the protocol hook after the rebalance period gets closed similar to how it gets called in
openRebalancePeriod, just before the rebalance gets opened.Resolution
Umami Team: Acknowledged.
-
LCY-6 Low Rebalance DoS With Empty Deposit Amounts DoS Resolved
Description
Before depositing on GMX, the amount of asset vault native asset to expend to acquire the required amount of GM is calculated:
uint256 assetAmountRequired = _previewGmMint(markets[i],gmSharesRequired[i], assetToMintFrom);The
assetAmountRequiredcan possibly be zero (post-internal netting), resulting in a GMX revert upon deposit creation withEmptyDepositAmounts.Because the mints are done in a for-loop, a failure in one market will cause all others to fail. Consequently, thecyclewill fail and the necessary GMI shares will not be minted.Recommendation
Carefully select the weights and target allocations such that function
_previewGmMintdoes not return a zero amount.Resolution
Umami Team: The issue was resolved in commit c07078c and 4467ccd.
-
VF-5 Low Precision Loss When Calculating Fees Precision Acknowledged
Description
When calculating the performance and management fee, division occurs before multiplication, leading to some precision loss, which makes the fee less than it should be.
Recommendation
Perform all division after all multiplication.
Resolution
Umami Team: Acknowledged.
-
GLOBAL-3 Low Redundant Code Superfluous Code Resolved
Description
Both
AggregateVaultHelper::_getVaultGmiandLibAggregateVaultUtils::getVaultGmiimplement the same functionality, with the only difference being how they fetch from storage.Recommendation
Reuse the same functionality to avoid duplicative code.
Resolution
Umami Team: The issue was resolved in commit ea5e0e7.
-
LAVU-1 Low Sum Of Vault’s GMI Less Than Total Supply Precision Acknowledged
Description
It is possible for the sum of the two asset vaults’ GMI allocations to be less than the total supply of GMI. As a result, a small portion of GMI is unaccounted for in the Asset Vaults which may lead to issues such as needing to expend more asset funds to reach the target allocation.
Recommendation
Clearly document this precision loss.
Resolution
Umami Team: Acknowledged.
-
AGVH-2 Low TVL Not Equal To PPS Multiplied By Shares Precision Resolved
Description
It is possible for the
TVL Of a Vault != PPS * totalSupplybecause of precision loss when calculating the price per share. This may lead to slight differences in the amounts withdrawn and the shares received for a deposit, although this is preferable compared to having thePPS * totalSupplypotentially exceeding the TVL.Recommendation
Clearly document this precision loss.
Resolution
Umami Team: The issue was resolved in commit b919f11.
-
RH-10 Low Total Supply Does Not Increase After Deposit Precision Resolved
Description
During a deposit it is checked that the shares to be minted is not 0:
require((shares =previewDeposit(assets)) != 0, "ZERO_SHARES");However, it is still possible for a user to mint 0 shares with a non-zero deposit due to the precision loss that occurs in
uint256 shares = assetsSansFees * (10 ** decimals) / pps;Consequently, a user may deposit a small amount of funds but receive 0 shares.Recommendation
Consider enforcing a minimum deposit amount and/or validating the request upon execution.
Resolution
Umami Team: The issue was resolved in commit 54ada0e.
-
GVH-2 Low Missing Minimum Output Amount On GMX Operations Logical Error Acknowledged
Description
In both the
mintGmTokensandburnGmTokensfunctions, a minimum output amount is set to 0. This means that regardless of how many tokens are returned on a mint or burn, the execution will be completed successfully.However, this poses a problem as any amount lost will negatively affect the vault's TVL, which would, in turn, impact the value of the users' shares.
Although some value loss is inevitable when making deposits or withdrawals on GMX v2, precautions should be taken to limit how much value can be lost.
This is especially important considering that the price impact can take out an unexpected amount of funds, and without a defined minimum output amount, price impact can remove a significant amount of value from the mint or burn, leading to a loss of funds for the users.
Recommendation
Use a non-zero minimum output amount for both minting and burning to limit the users' loss.
Resolution
Umami Team: Acknowledged.
-
LCY-7 Low Unused Output Amount Leads to Skew Logical Error Acknowledged
Description
In the
rebalanceGmifunction, ifisOppositeDirectionis true, it attempts to swap one token for the other to rebalance before the minting and burning processes. This is achieved by swapping whichever token is at a surplus for the other.Any swap will experience slippage resulting in the output amount being different than the input amount. The issue is that when updating the
_current[]as well as calling the function_commitGmiDeltaProportionsfor each of USDC and ETH, onlyinternalNetis used.This will lead to a skew in the accounting because the actual delta of the output token will be different than that of the input token.
This skew will lead to the wrong amount being minted/burned as the
cyclefunction continues, as well as an undervaluing of whichever asset was the input token at the expense of the other token.Recommendation
Change the state of the outputted token based on the returned output amount of the swap, instead of the inputted amount. This will align the actual token balance proportions with what is stored in the state, preventing misvaluing of assets and inaccurate minting/burning.
Resolution
Umami Team: We do this so the vault receiving the swap is responsible for all fees incurred from the swap (slippage and trading fee).
-
VF-6 Low Management Fees Deducted Based On Performance Logical Error Acknowledged
Description
When calculating the withdrawal fee, the management fee component is incorrectly deducted only if the current vault price per share (PPS) is higher than the watermark PPS.
This condition is required for the performance fee, not for the management fee. This behavior is inconsistent with the way the management fee is deducted when rebalancing and leads to fewer fees for the protocol overall.
Recommendation
Calculate the management fee where there is a profit, regardless of the current vault price per share.
Resolution
Umami Team: We take the performance and management fee on regular intervals at rebalance time. On withdrawal we only take them if there has been a profit since we last took them.
-
LCY-8 Low State Does Not Unwind Properly On Failed Fulfillments Logical Error Acknowledged
Description
When a rebalance occurs, and the protocol intends to mint GM tokens, it will deposit either WETH or USDC into GMX via the
mintGmTokensfunction.After the deposit is created, the GMX Keeper will execute the order, and on completion, send the GM tokens back to the Umami protocol. If the order is canceled or fails on execution for any reason, the GMX keeper will return the long or short token to Umami.
Because the function
_fulfilMintRequestwill loop through all of the_mintRequest, if one did not succeed then the whole transaction will revert due to the following require statementif(!depositRequestDetails.success) revert RequestNotSucceded();.Anytime the
depositExecutionon GMX v2 fails or anytime an order is canceled, those funds will require manual intervention. There are a variety of reasons both maliciously and unintended that can cause an order to fail upon execution.For example, the max deposit cap can be exceeded during execution and not creation, as well as congestion on the network leading to the execution of the order taking place beyond the max block limit.
Recommendation
Carefully monitor the status of a rebalance and fix state inconsistencies with handlers when necessary.
Resolution
Umami Team: The keeper runs a simulation before sending all the transactions on chain but the possibility of them still failing after simulations is always there because of the state changes on chain between simulation block and rebalance block. If any of the requests fail it’d require manual intervention.
-
RH-11 Low Revert Bytes Gas Griefing Gas Griefing Acknowledged
Description
In the
afterDepositExecutionandafterWithdrawalExecutionfunctions in the catch block, arbitrary bytes are loaded into memory from the arbitrary request callback.Although the callback is limited in gas expenditure by the
executionGasAmountCallback, the arbitrary callback contract can cause the keeper to expend much more gas than expected by reverting with a large amount of revert bytes which are then subsequently loaded into memory.Memory expansion costs a quadratic amount of gas and malicious revert bytes can lead to the keeper expending hundreds of thousands or even millions of additional unexpected gas units. Such an expenditure can cost the keepers a significant amount over a period of time.
Recommendation
Do not accept revert bytes from the callback contract.
Resolution
Umami Team: The eth_call is going to fail and keeper will continue on with the next one without actually sending any transaction and causing any loss of funds. The
gasfield for the transaction will be set to the base gas unitsexecutionGasAmount+ callback gas unitsexecutionGasAmountCallbackwhich will make it go OOG. -
GLOBAL-4 Low Lacking onlyDelegateCall Modifier Modifiers Acknowledged
Description
There are several inconsistencies with contracts that have the functionality needed to be called on its own or delegate-called into. Functions that work only when delegate called-into require the
onlyDelegateCallmodifier will not return an invalid result when called into.- For the
GmxV2Handlercontract: getDepositRequestDetailsandgetWithdrawRequestDetailsneed theonlyDelegateCallmodifier- For the
VaultFeescontract: getDepositFee,getVaultRebalanceFees, andgetWithdrawalFeeneed theonlyDelegateCallmodifier- consider making the following public functions internal, since they are called only from within the contract and also work only when delegate-called into:
_getVaultDepositFee,_getVaultWithdrawalFee,getVaultTVL, andgetVaultPPS - For the
AggregateVaultHelpercontract: getTotalNotionalneeds theonlyDelegateCallmodifier- all functions from the
AggregateVaultViewscontract require theonlyDelegateCallmodifier but with the sole exception ofvaultToAssetVaultIndex, all other functions the not used by theAggregateVaultHelpercontract.
Recommendation
Add an
onlyDelegateCallmodifier to the mentioned functions and implement the other suggested changes. Consider makingvaultToAssetVaultIndexan internal function in theAggregateVaultHelpercontract and removing theAggregateVaultViewscontract completely.If
AggregateVaultViewsis to be kept for on-chain reading of values through multicall, theonlyDelegateCallmodifier must be added to all of its functions.Resolution
Umami Team: Acknowledged.
- For the
No findings match.
Invariants 16
The review's fuzzing suite asserted 16 invariants. 13 held and 3 did not.
Every invariant tested
| ID | Invariant | Result |
|---|---|---|
AV-01 | TVL of a Vault = PPS * Share Supply | Broken |
AV-02 | Sum of User Vault Share Balances Does Not Exceed Total Supply | Held |
AV-03 | Zero Address Vault Share Balance is Zero | Held |
AV-04 | Total supply of Asset Vault Not Modified Until Request Is Executed | Held |
AV-05 | Asset Vault Shares Decreased By Amount Requested on Redeem | Held |
AV-06 | TVL Does Not Increase After A Rebalance Without Price Change | Held |
AV-07 | Total Supply Increased After Deposit | Broken |
AV-08 | Assets Increased By Amount Deposited | Held |
AV-09 | Previewed Shares = Minted Shares Without Price Movement | Held |
AV-10 | Total Supply Decreased After Redeem | Held |
AV-11 | Shares Decreased By Amount Redeemed | Held |
AV-12 | Previewed Assets = Redeemed Assets Without Price Movement | Held |
GLOBAL-01 | Rebalance Brings Allocation Closer to Target than Original | Held |
GMI-01 | Sum of GMI Balances Of Both Vaults = GMI Total Supply | Broken |
GMI-02 | GMI Balance of Aggregate Vault Does Not Exceed GMI Total Supply | Held |
GMI-03 | Zero Address GMI Share Balance is Zero | Held |
More from Umami
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.