Omniscia Ultra Yield Audit
UltraFeeder Manual Review Findings
UltraFeeder Manual Review Findings
UFR-01M: Inefficient Redemption Fulfilment Structure
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | UltraFeeder.sol:L186-L195, L198-L208, L212-L222 |
Description:
The referenced redemption fulfilment function structure is complicated unnecessarily, relying on before, after, as well as extra after-execution logic for redemption fulfilments.
Impact:
While this is a gas optimization, the complexity of the code is prone to error and must be simplified.
Example:
src/vaults/UltraFeeder.sol
184/// @dev Before fulfill redeem - transfer funds from fundsHolder to vault185/// @dev "assets" will already be correct given the token user requested186function beforeFulfillRedeem(address _asset, uint256 assets, uint256 shares) internal override {187 IUltraVault mainVault_ = mainVault();188 // Fulfill redeem in main vault. Returns asset units189 mainVault_.fulfillRedeemOfAsset(_asset, shares, address(this));190 uint256 mainAssetsClaimed = mainVault_.redeemAsset(_asset, shares, address(this), address(this));191
192 // Deduct the expected withdrawal fee from the total amount of assets193 uint256 expectedAssetsAfterFees = assets - mainVault_.calculateWithdrawalFee(assets);194 require(mainAssetsClaimed == expectedAssetsAfterFees, AssetNumberMismatch());195}196
197/// @dev Internal fulfill redeem request logic198function _fulfillRedeemOfAsset(199 address _asset,200 uint256 assets,201 uint256 shares,202 address controller203) internal override returns (uint256) {204 uint256 assetsFulfilled = super._fulfillRedeemOfAsset(_asset, assets, shares, controller);205 // Deduct the expected withdrawal fee from the total amount of assets206 uint256 withdrawalFee = mainVault().calculateWithdrawalFee(assetsFulfilled);207 return assetsFulfilled - withdrawalFee;208}209
210/// @dev Hook for inheriting contracts after fulfill redeem211/// @dev Correct claimable redeem amounts to account for underlying vault fees212function afterFulfillRedeem(213 address _asset,214 uint256 assets,215 uint256, // shares216 address controller217) internal override {218 // The base implementation has added `_assets` to the claimable assets219 // We need to calculate the withdrawal fee and deduct it from the claimable assets220 uint256 withdrawalFee = mainVault().calculateWithdrawalFee(assets);221 _consumeClaimableRedeem(controller, _asset, withdrawalFee, 0);222}Recommendation:
We advise the code of the BaseControlledAsyncRedeem::_fulfillRedeemOfAsset function to be updated to utilize a return value by the UltraFeeder::beforeFulfillRedeem function, permitting the actual assets to be redeemed to be yielded and thus simplifying the code greatly.
Alleviation (28f27853965de07fb79f4f2b5fed696d35120032):
The Ultra Yield team evaluated this exhibit and opted to not implement the optimization outlined as it would significantly affect the logic of the UltraVault and UltraFeeder contracts.
