sherlock-2025-06-symbiotic-relay-m03

[M-03] autoDeployedVault mapping is not updated after unregisterOperatorVault

보고서

Summary

OpNetVaultAutoDeploy 익스텐션을 사용하면 오퍼레이터를 등록했을 때 볼트를 자동으로 배포해준다. 자동배포된 볼트는 오퍼레이터 별 AutoDeployedVault 에 기록된다. 하지만 볼트를 삭제할 때는 오퍼레이터 별 AutoDeployedVault 기록을 갱신하지 않는다. 이로 인해 오퍼레이터 재등록 시 새로운 볼트를 배포해주지 않으며, getAutoDeployedVault 함수는 해지된 볼트 주소를 리턴한다.

Keyword

wrong state, logic flaw

Vulnerability

OpNetVaultAutoDeploy 익스텐션을 사용하면 오퍼레이터를 등록했을 때 볼트를 자동으로 배포해준다. 자동배포된 볼트는 오퍼레이터 별 AutoDeployedVault 에 기록된다. 이미 해당 오퍼레이터 소유의 AutoDeployedVault 가 배포되어 기록되었다면 이후에는 배포하지 않는다.

function _registerOperatorImpl(
    address operator
) internal virtual override {
    super._registerOperatorImpl(operator);
@>  if (isAutoDeployEnabled() && getAutoDeployedVault(operator) == address(0)) {
@>      (address vault, address delegator,) = OpNetVaultAutoDeployLogic.createVault(operator);
        _registerOperatorVault(operator, vault);
        if (isSetMaxNetworkLimitHookEnabled()) {
            ISetMaxNetworkLimitHook(NETWORK()).setMaxNetworkLimit(
                delegator, SUBNETWORK_IDENTIFIER(), type(uint256).max
            );
        }
    }
}
 
// OpNetVaultAutoDeployLogic
function createVault(
    address operator
) public returns (address vault, address delegator, address slasher) {
    ...
 
    (vault, delegator, slasher) = createVault(
        version, address(0), vaultParams, delegatorIndex, delegatorParams, withSlasher, slasherIndex, slasherParams
    );
@>  _getOpNetVaultAutoDeployStorage()._autoDeployedVault[operator] = vault;
}

unregisterOperatorVault 로 오퍼레이터의 볼트를 삭제했을 때, 오퍼레이터 별 AutoDeployedVault 기록은 삭제하지 않는다. 따라서 getAutoDeployedVault 함수는 더이상 사용되지 않는 볼트를 리턴하게 된다. 또한, 오퍼레이터를 다시 등록하더라도 새로운 볼트를 배포해주지 않는다.

function unregisterOperatorVault(address operator, address vault) public {
    IVotingPowerProvider.VotingPowerProviderStorage storage $ = _getVotingPowerProviderStorage();
    if (!$._operatorVaults[operator].remove(Time.timestamp(), vault)) {
        revert IVotingPowerProvider.VotingPowerProvider_OperatorVaultNotRegistered();
    }
    $._allOperatorVaults.remove(Time.timestamp(), vault);
 
    emit IVotingPowerProvider.UnregisterOperatorVault(operator, vault);
}

Impact

getAutoDeployedVault 함수가 더이상 사용되지 않는 볼트를 리턴한다. 오퍼레이터를 다시 등록하더라도 새로운 AutoDeployedVault 를 배포해주지 않는다.

Mitigation

볼트를 해지할 때 AutoDeployedVault 기록도 업데이트 한다.

Memo

일단 이상하면 다 제출하는 게 맞는 전략인 것 같다.


tags: bughunting, symbiotic, smart contract, solidity, severity medium, wrong state, logic flaw