Assessment reports>Cove>Discussion>Weights can pass unchecked

Weights can pass unchecked

The rebalancing mechanism allows the conversion and recalculation of the basket's multiple assets. When it happens, the rebalance passes through multiple stages of the contract. The completeRebalance finalizes the rebalance procedures if all the conditions are met.

function completeRebalance(
    BasketManagerStorage storage self,
    ExternalTrade[] calldata externalTrades,
    address[] calldata baskets,
    uint64[][] calldata basketTargetWeights
)
    external
{
    if (self.rebalanceStatus.status == Status.NOT_STARTED) {
        revert NoRebalanceInProgress();
    }
    _validateBasketHash(self, baskets, basketTargetWeights);
    // Check if the rebalance was proposed more than 15 minutes ago
    // slither-disable-next-line timestamp
    if (block.timestamp - self.rebalanceStatus.timestamp < 15 minutes) {
        revert TooEarlyToCompleteRebalance();
    }
    // if external trades are proposed and executed, finalize them and claim results from the trades
    if (self.rebalanceStatus.status == Status.TOKEN_SWAP_EXECUTED) {
        if (keccak256(abi.encode(externalTrades)) != self.externalTradesHash) {
            revert ExternalTradeMismatch();
        }
        _processExternalTrades(self, externalTrades);

    }

    uint256 len = baskets.length;
    uint256[] memory totalValue_ = new uint256[](len);
    // 2d array of asset amounts for each basket after all trades are settled
    uint256[][] memory afterTradeAmounts_ = new uint256[][](len);
    _initializeBasketData(self, baskets, afterTradeAmounts_, totalValue_);
    // Confirm that target weights have been met, if max retries is reached continue regardless
    if (self.retryCount < _MAX_RETRIES) {
        if (!_isTargetWeightMet(self, baskets, afterTradeAmounts_, totalValue_, basketTargetWeights)) {
            self.retryCount += 1;
            self.rebalanceStatus.timestamp = uint40(block.timestamp); 
            self.externalTradesHash = bytes32(0);
            self.rebalanceStatus.status = Status.REBALANCE_PROPOSED;
            return;
        }
    }
    _finalizeRebalance(self, baskets);

}

Notice that if the target weights are not met, the function will retry the rebalance process up to _MAX_RETRIES times. However, the function does not revert if the target weights are not met after the maximum number of retries, and it simply finalizes the rebalance process with whatever weights were used. This could lead to a scenario where unfavorable weights are used to finalize the rebalance process, which could lead to a basket that is not properly balanced.

Due to the current mechanism (as of the time of writing) of continuing the process of external trades even if the target weights are not met, the rebalance process cannot be reverted altogether. We thus leave this section for potential future consideration, as the current mechanism is intended. Should future design changes allow for a cancelation of the rebalance process, it would be important to revert in the case of unfavorable weights.

Zellic © 2025Back to top ↑