Missing mapping update in BasketToken.updateBitFlag()
causes rebalancing failure
Description
The updateBitFlag()
function does not update the basketAssetToIndexPlusOne
mapping after adding a new asset to the registry. This leads to errors during rebalancing when the rebalance cannot locate the new asset.
function updateBitFlag(address basket, uint256 bitFlag) external onlyRole(_TIMELOCK_ROLE) {
// Checks
// Check if basket exists
uint256 indexPlusOne = _bmStorage.basketTokenToIndexPlusOne[basket];
if (indexPlusOne == 0) {
revert BasketTokenNotFound();
}
uint256 currentBitFlag = BasketToken(basket).bitFlag();
if (currentBitFlag == bitFlag) {
revert BitFlagMustBeDifferent();
}
// Check if the new bitFlag is inclusive of the current bitFlag
if ((currentBitFlag & bitFlag) != currentBitFlag) {
revert BitFlagMustIncludeCurrent();
}
address strategy = BasketToken(basket).strategy();
if (!WeightStrategy(strategy).supportsBitFlag(bitFlag)) {
revert BitFlagUnsupportedByStrategy();
}
bytes32 newId = keccak256(abi.encodePacked(bitFlag, strategy));
if (_bmStorage.basketIdToAddress[newId] != address(0)) {
revert BasketIdAlreadyExists();
}
// Remove the old bitFlag mapping and add the new bitFlag mapping
bytes32 oldId = keccak256(abi.encodePacked(currentBitFlag, strategy));
_bmStorage.basketIdToAddress[oldId] = address(0);
_bmStorage.basketIdToAddress[newId] = basket;
_bmStorage.basketAssets[basket] = AssetRegistry(_bmStorage.assetRegistry).getAssets(bitFlag);
emit BasketBitFlagUpdated(basket, currentBitFlag, bitFlag, oldId, newId);
// Update the bitFlag in the BasketToken contract
BasketToken(basket).setBitFlag(bitFlag);
}
function basketTokenToRebalanceAssetToIndex(
BasketManagerStorage storage self,
address basketToken,
address asset
)
public
view
returns (uint256 index)
{
index = self.basketAssetToIndexPlusOne[basketToken][asset];
if (index == 0) {
revert AssetNotFoundInBasket();
}
unchecked {
// Overflow not possible: index is not 0
return index - 1;
}
}
Here is an error scenario.
A new asset is added to the
AssetRegistry
.Then,
BasketManager.updateBitFlag()
is called to enable the new asset. ThebasketAssetToIndexPlusOne
mapping does not include the index of the new asset because it is only initialized increateNewBasket()
.During rebalancing, it calls
basketTokenToRebalanceAssetToIndex()
via_processInternalTrades()
inproposeTokenSwap()
. This causes anAssetNotFound
error as the new asset's index is missing frombasketAssetToIndexPlusOne
.The rebalancing process fails, and the new asset cannot be included in the rebalance.
Impact
The current fee-calculation method leads to undercollected fees, causing financial loss to the protocol and underperformance of fee collection for the protocol.
Recommendations
Modify the updateBitFlag()
function to ensure the basketAssetToIndexPlusOne
mapping is updated whenever a new asset is added.
Remediation
This issue has been acknowledged by Storm Labs, and a fix was implemented in commit 9e67b7b8↗.