Baseline engaged Guardian to review the security of its market making looping updates. From the 27th of January to the 3rd of February, a team of 6 auditors reviewed the source code in scope.
- Published
- Review window
- January 27 to February 3, 2025
- Language
- Solidity
- Chains
- Base
- Sector
- Token launches
- 1 Critical
- 1 High
- 3 Medium
- 13 Low
- 0 Informational
Scope
Overview
Baseline engaged Guardian to review the security of its market making looping updates. From the 27th of January to the 3rd of February, a team of 6 auditors reviewed the source code in scope.
Issues Detected Throughout the engagement 2 High/Critical issues were uncovered and promptly remediated by the Baseline team.
Findings 18
-
C-01 Critical Mishandled Donated Liquidity Breaks Rebalance DoS Resolved
Description
Proof of concept: PoC
The
removeAllFromfunction in theBPOOLv1contract is intended to handle any unexpected liquidity (i.e., donations) by removing it from the position and then accounting for both the liquidity amount and its fees in the totalbAssetFees_, which is subsequently sent to the fee recipient.However, the current implementation does not behave as intended. Instead, the donated amount is burned in the internal
removeLiquidityfunction while that same burned amount is also counted inbAssetFees.This discrepancy causes the
MarketMakingcontract’sbAssetbalance to be lower than the totalbAssetFees_, leading to a revert when fees are transferred.Because of this revert, the rebalance function fails to execute, preventing the protocol from adjusting its liquidity to changing market conditions. Additionally, a malicious actor could intentionally exploit this flaw by donating, causing a DOS to the rebalance function.
Recommendation
Modify the implementation so that the donated liquidity is not burned and is correctly accounted for and transferred to the fee recipient.
Resolution
Baseline Team: Resolved.
-
H-01 High Guaranteed Profit By Rebalancing Gaming Partially resolved
Description
Proof of concept: PoC
The
rebalancefunction removes the protocol-owned liquidity, updates ticks, and redeploys liquidity using new tick ranges. The_updateTickslogic determines theanchorTickbased on whether the anchor liquidity is higher or lower than the discovery liquidity.There are immediate arbitrage opportunities by using the rebalance functionality. When the anchor liquidity is higher than the discovery liquidity:
- The upper anchor tick is below the active tick, and the current price is within the discovery range.
- User can buy a large amount of
bToken, pushing the price even higher. - Call
rebalance, which updates ticks and range liquidities. - Sell the same amount of
bTokenat a higher average price due to higher anchor liquidity.
A similar arbitrage opportunity can occur in the opposite direction as well. When anchor liquidity is lower than discovery liquidity, the user can sell, rebalance, and buy back. This time, the user sells in a low-liquidity environment and buys back in a high-liquidity environment.
Recommendation
Consider rate limiting the amount of ticks that can be dropped at a time to limit the scale of this arbitrage vector.
Resolution
Baseline Team: Partially Resolved by rate limiting the arbitrage.
-
M-01 Medium Mismatching Baseline Values Logical Error Acknowledged
Description
After the updates, the
blvis calculated based on the upper floor tick in theMarketMaking,CreditFacility, andLoopFacilitycontracts. However, in the BPOOL contract, it is still calculated using the lower floor tick.The
BaselineInit.launchfunction uses the baseline value from the BPOOL contract when calculating capacity, which creates a discrepancy.Recommendation
Update the
getBaselineValuefunction in BPOOL contract.Resolution
Baseline Team: Acknowledged.
-
M-02 Medium updateTicks Should Use Previous Liquidity Logical Error Acknowledged
Description
In the
_updateTicksfunction of theMarketMakingpolicy there is logic to alter the upper tick of the anchor position based upon the liquidity of the Discovery position relative to the Anchor position liquidity.This is done to ideally prevent
bAssetsupply being minted in excess of the Discovery position liquidity in the range above the price in the Anchor position.This reduces the magnitude of an arbitrage opportunity that would arise from selling through the Discovery range into the Anchor range and benefitting from the increased liquidity due to a higher leverage of the Anchor.
However in such an arbitrage scenario, the liquidity that the Anchor position should be compared against is the liquidity of the previous Discovery position rather than the liquidity of the new Discovery position.
This is because the old Discovery position is the one which is sold through to reach the new Anchor range and thus trigger the rebalance and therefore is the liquidity which the Arbitrage economics are based upon.
As a result the Anchor range upper tick handling should consider the liquidity of the old Discovery position rather than the current result of the
_getThresholdLiquidityfunction, which will be the new Discovery position liquidity.Recommendation
Consider comparing the predicted anchor liquidity against the minimum of both the result of the
_getThresholdLiquidityand the old Discovery position liquidity to be the most conservative in limiting the arbitrage opportunities from selling through the Discovery position.Resolution
Baseline Team: Acknowledged.
-
M-03 Medium DISCOVERY_LENGTH Hardcoded For 1% Fee Pools Logical Error Acknowledged
Description
The
DISCOVERY_LENGTHvariable in theMarketMakingcontract is intended to represent 30 tick spacings, as indicated by the comment.However, its current implementation as 30 x 200 only aligns with 1% fee pools. This means the calculation will be incorrect if applied to pools with different fee tiers.
Recommendation
If the protocol intends to only use 1% fee pools, no changes are needed. Otherwise, it should obtain the correct tick spacing from the
BPOOLv1contract instead.Resolution
Baseline Team: Acknowledged.
-
L-01 Low Unnecessary Permission Request Superfluous Code Resolved
Description
The
MarketMakingcontract requests permission forBPOOL.mintfunction. However, this function is not called fromMarketMakingafter updates, and the permission request can be removed.Recommendation
Consider removing unnecessary permission request.
Resolution
Baseline Team: Resolved.
-
L-02 Low Unused Error Superfluous Code Resolved
Description
NotOwnererror in theMarketMakingcontract is defined but never used.Recommendation
Remove unused error.
Resolution
Baseline Team: Resolved.
-
L-03 Low Unused Tick Bounds In canRebalance Superfluous Code Resolved
Description
The
canRebalancefunction retrieves the anchor range bounds using the_getTSBoundsfunction. However, these ticks are not utilized incanRebalance, asrebalanceTicksare used to determine price movement.Recommendation
Consider removing unused ticks and the
_getTSBoundscall.Resolution
Baseline Team: Resolved.
-
L-04 Low Active Tick Is Not Checked During Configuration Validation Resolved
Description
In the previous version of the contract, the active tick was required to be within the anchor range during configuration.
Currently, the
MarketMaking.configureDependenciesfunction still retrieves the lower and upper ticks of the anchor range.However, unlike before, these ticks are no longer used for comparison against the active tick and remain unutilized.
Recommendation
If the active tick must be within the anchor range, compare it against the anchor range ticks. Otherwise, remove the
BPOOL.getTicks(Range.ANCHOR)call from the function.Resolution
Baseline Team: Resolved.
-
L-05 Low Incorrect Comment Documentation Resolved
Description
BUMPABLE_PREMIUMis set to 1500, with a comment stating "1500 tick spacings". However, this value represents only 1500 ticks, not 1500 tick spacings. Based on the current setup, it corresponds to 7.5 tick spacings.Recommendation
Update to comment to 1500 ticks.
Resolution
Baseline Team: Resolved.
-
L-06 Low Unused Import Superfluous Code Resolved
Description
SafeCastLiblibrary is imported inMarketMakingcontract but never used.Recommendation
Consider removing unused imports.
Resolution
Baseline Team: Resolved.
-
L-07 Low Liquidity Donations Not Tracked In New Ranges Logical Error Acknowledged
Description
The rebalance process removes liquidity from all existing ranges, checks for donations during this step, and accounts for them as fees before updating the ranges and re-adding liquidity.
However, when liquidity is added back to the new ranges, previously donated amounts in these new ranges are not accounted for.
As a result, in
_deployLiquidity, if the discovery range had prior donations, the buffer amount and consequently theanchorReserveswill differ from the values in_updateTicks, which were used to adjust the next rebalance ticks andanchorTick.This discrepancy can lead to imbalances in the rebalancing logic, introducing inefficiencies in the system. For example,
_updateTicksensures that the anchor does not include the current price when anchor liquidity exceeds discovery liquidity.However, a prior donation could cause this condition to be met, resulting in an extra minted supply and potentially creating an arbitrage opportunity for users, though its profitability may be limited.
Recommendation
Remove any unexpected extra liquidity as fees before adding liquidity to the new ranges to ensure consistency in rebalancing calculations.
Resolution
Baseline Team: Acknowledged.
-
L-08 Low Liquidity Position DOS In Extreme Ticks DoS Acknowledged
Description
In very extreme ticks such as -557658 an attacker would only need 300 ETH to fill all available liquidity (
liquidityGross). This would make it impossible to rebalance a position in this range.Therefore, if the initial active tick is around this tick, an attacker could prevent BVL from ever increasing. The same issue can occur in higher ticks for
bAsset, where a user can buy manybAssetswhen they have a low price and DOS a liquidity range in high ticks.Making a barrie on how high discovery range can rise before hitting the maxed out liquidity. We would need approximately 1 billion
bAssetsto DOS the liquidity range between tick 317273 and 317273 - 200 for example.Recommendation
We recommend the team to be aware that having a very low
INITIAL_ACTIVE_TICK(such as-
- would make it possible to either DOS a nearby liquidity position using ETH reserves or
make it possible for someone to gather enough
bAssetsto DOS a liquidity range in the higher tick ranges such as 317273, preventing the price to go further up.Ultimately, putting a hardcoded limitation on the minimum
INITIAL_ACTIVE_TICKwould prevent this issue.Resolution
Baseline Team: Acknowledged.
-
-
L-09 Low Unecessary Min() Superfluous Code Resolved
Description
The
ifstatement on line 414 already implies thatanchorReserves_ > totalReserves -_getVirtualReserves(). Therefore the use ofmin()is redundant.Recommendation
We recommend refactoring line 415 by removing
min()and settinganchorReserves_to the difference:anchorReserves_ = totalReserves - _getVirtualReserves());Resolution
Baseline Team: Resolved.
-
L-10 Low Tick And sqrtPrice Misalignment Side Effects Warning Acknowledged
Description
When swapping
zeroToOnean edge scenario makes it so active tick andsqrtPricecan be misaligned, with tick being one less than what is should.This could lead to rebalancing positions based on the wrong tick value. Specifically, using one tick lower than what it should (in relation to
sqrtPrice). This ultimately makes it so the positions would be assigned to a lower range than what it should.Although we could not find a scenario where this is harmful for the protocol, we believe it has potential to do so in unforeseen circumstances or with further development.
Recommendation
We recommend the Baseline team to be mindful of this scenario as they implement new features or change the current ones.
Resolution
Baseline Team: Acknowledged.
-
L-11 Low Missing Anchor Range Warning Acknowledged
Description
When resetting the ticks for the ranges, it is possible for the anchor range to have the same value in the upper tick and lower tick.
addReservesTo()will prevent a revert from happening by returning early, but this will lead to no liquidity deployed for the anchor range.Recommendation
Consider shifting the upper and lower ticks if they are equal, so that the anchor range can be deployed.
Resolution
Baseline Team: Acknowledged.
-
L-12 Low Remove Console Import Superfluous Code Resolved
Description
BPOOL.v1.solandMarketMakingimports theconsole2library. Console logging can be removed for production since it is unnecessary.Recommendation
Remove the imports of the
console2library.Resolution
Baseline Team: Resolved.
-
L-13 Low getCirculatingSupply Incorrect For Time Logical Error Acknowledged
Description
The
getCirculatingSupplyview function in theMarketMakingcontract does not account for collateral in the Loops facility which is burned with time passing.However the corresponding
getTotalCapacityfunction will account for the capacity change with respect to time resulting from theLOOPS.totalDebt().This may mislead users and integrators and increases the risk of this function causing bugs in the future due to this undocumented behavior.
Recommendation
Consider adjusting the
getCirculatingSupplyfunction to account for the collateral in the Loops facility which is yet to be burnt.Resolution
Baseline Team: Acknowledged.
No findings match.
More from Baseline Markets
All 12 reports-
Mercury, Round 3
109 findings4 critical · 9 high 109 findings: 4 critical, 9 high, 28 medium, 33 low, 35 informational -
AMM, Round 2
47 findings4 critical · 14 high 47 findings: 4 critical, 14 high, 8 medium, 13 low, 8 informational -
AMM
54 findings3 critical · 6 high 54 findings: 3 critical, 6 high, 13 medium, 11 low, 21 informational -
Fixed Supply
34 findings4 high 34 findings: 4 high, 10 medium, 20 low
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.
