Omniscia 0xPhase Audit
BalancerAccountingFacet Code Style Findings
BalancerAccountingFacet Code Style Findings
BAF-01C: Inefficient Evaluation of Transfer Fulfilment
Type | Severity | Location |
---|---|---|
Gas Optimization | BalancerAccountingFacet.sol:L146-L149, L177-L180 |
Description:
The for
loops within BalancerAccountingFacet::withdraw
will evaluate whether the expected transfer is fulfillable at the start of each loop rather than after each withdrawal is performed.
Example:
134if (asset.balanceOf(address(this)) >= amount) {135 asset.safeTransfer(msg.sender, amount);136 return amount;137}138
139(Offset[] memory arr, , ) = _calculations().offsets(asset);140
141require(arr.length > 0, "BalancerInitializer: No yields on withdraw");142
143uint256 acc = amount - asset.balanceOf(address(this));144
145for (uint256 i = 0; i < arr.length; ) {146 if (acc == 0) {147 asset.safeTransfer(msg.sender, amount);148 return amount;149 }150
151 Offset memory offset = arr[i];152 Yield storage yield = s.yield[offset.yieldSrc];153
154 if (!offset.isPositive || !yield.state) {155 unchecked {156 i++;157 }158
159 continue;160 }161
162 _updateAPR(offset.yieldSrc);163
164 uint256 yieldAmount = MathLib.min(offset.offset, acc);165
166 offset.yieldSrc.withdraw(yieldAmount);167
168 yield.lastDeposit = offset.yieldSrc.totalBalance();169
170 unchecked {171 acc -= yieldAmount;172 i++;173 }174}
Recommendation:
We advise the referenced code blocks to be relocated after the acc
subtractions, ensuring that they are performed as efficiently as possible.
Alleviation:
The withdrawal flow of the balancer was updated as advised, evaluating whether the function should stop right after subtracting the accumulator variable rather than at the start of each loop.
BAF-02C: Optimization of Yield Status Evaluation
Type | Severity | Location |
---|---|---|
Gas Optimization | BalancerAccountingFacet.sol:L53, L80, L154, L186 |
Description:
The BalancerAccountingFacet::deposit
and BalancerAccountingFacet::withdraw
functions will inefficiently evaluate the state
of a yield source by accessing storage when the BalancerCalculationsFacet::offsets
has already performed this check.
Example:
145for (uint256 i = 0; i < arr.length; ) {146 if (acc == 0) {147 asset.safeTransfer(msg.sender, amount);148 return amount;149 }150
151 Offset memory offset = arr[i];152 Yield storage yield = s.yield[offset.yieldSrc];153
154 if (!offset.isPositive || !yield.state) {155 unchecked {156 i++;157 }158
159 continue;160 }161
162 _updateAPR(offset.yieldSrc);163
164 uint256 yieldAmount = MathLib.min(offset.offset, acc);165
166 offset.yieldSrc.withdraw(yieldAmount);167
168 yield.lastDeposit = offset.yieldSrc.totalBalance();169
170 unchecked {171 acc -= yieldAmount;172 i++;173 }174}
Recommendation:
As the BalancerCalculationsFacet::offsets
code will assign an offset
of 0
if the yield's state is not active, we advise the code to evaluate offset.offset
rather than yield.state
, allowing the storage
declaration to be relocated after the if
clause and thus optimizing the code's gas cost whenever a particular yield source is meant to be skipped.
Alleviation:
The isPositive
member of a particular offset has been replaced by an offset state that can contain the additional state of None
, representing a yield source that should not be utilized. The code was updated to take advantage of this and thus has been made as optimal as possible, alleviating this exhibit.