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
- modules/voting-power/extensions/OpNetVaultAutoDeploy.sol#L92
- modules/voting-power/logic/VotingPowerProviderLogic.sol#L481
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