Skip to content

Commit

Permalink
Use swapFeeManager as owner in mev hook (#1305)
Browse files Browse the repository at this point in the history
  • Loading branch information
elshan-eth authored Feb 19, 2025
1 parent 3995e95 commit 01c1a9b
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 23 deletions.
4 changes: 2 additions & 2 deletions pkg/pool-hooks/contracts/MevCaptureHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ contract MevCaptureHook is BaseHooks, SingletonAuthentication, VaultGuard, IMevC
function setPoolMevTaxMultiplier(
address pool,
uint256 newPoolMevTaxMultiplier
) external withMevTaxEnabledPool(pool) authenticate {
) external withMevTaxEnabledPool(pool) onlySwapFeeManagerOrGovernance(pool) {
_setPoolMevTaxMultiplier(pool, newPoolMevTaxMultiplier);
}

Expand Down Expand Up @@ -279,7 +279,7 @@ contract MevCaptureHook is BaseHooks, SingletonAuthentication, VaultGuard, IMevC
function setPoolMevTaxThreshold(
address pool,
uint256 newPoolMevTaxThreshold
) external withMevTaxEnabledPool(pool) authenticate {
) external withMevTaxEnabledPool(pool) onlySwapFeeManagerOrGovernance(pool) {
_setPoolMevTaxThreshold(pool, newPoolMevTaxThreshold);
}

Expand Down
22 changes: 2 additions & 20 deletions pkg/pool-hooks/contracts/StableSurgeHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,6 @@ contract StableSurgeHook is BaseHooks, VaultGuard, SingletonAuthentication {
_;
}

modifier withPermission(address pool) {
_ensureValidSender(pool);
_;
}

// Store the current threshold for each pool.
mapping(address pool => uint256 threshold) private _surgeThresholdPercentage;

Expand Down Expand Up @@ -285,7 +280,7 @@ contract StableSurgeHook is BaseHooks, VaultGuard, SingletonAuthentication {
function setMaxSurgeFeePercentage(
address pool,
uint256 newMaxSurgeSurgeFeePercentage
) external withValidPercentage(newMaxSurgeSurgeFeePercentage) withPermission(pool) {
) external withValidPercentage(newMaxSurgeSurgeFeePercentage) onlySwapFeeManagerOrGovernance(pool) {
_setMaxSurgeFeePercentage(pool, newMaxSurgeSurgeFeePercentage);
}

Expand All @@ -297,23 +292,10 @@ contract StableSurgeHook is BaseHooks, VaultGuard, SingletonAuthentication {
function setSurgeThresholdPercentage(
address pool,
uint256 newSurgeThresholdPercentage
) external withValidPercentage(newSurgeThresholdPercentage) withPermission(pool) {
) external withValidPercentage(newSurgeThresholdPercentage) onlySwapFeeManagerOrGovernance(pool) {
_setSurgeThresholdPercentage(pool, newSurgeThresholdPercentage);
}

/// @dev Ensure the sender is the swapFeeManager, or default to governance if there is no manager.
function _ensureValidSender(address pool) private view {
address swapFeeManager = _vault.getPoolRoleAccounts(pool).swapFeeManager;

if (swapFeeManager == address(0)) {
if (_canPerform(getActionId(msg.sig), msg.sender, pool) == false) {
revert SenderNotAllowed();
}
} else if (swapFeeManager != msg.sender) {
revert SenderNotAllowed();
}
}

/**
* @notice Calculate the surge fee percentage. If below threshold, return the standard static swap fee percentage.
* @dev It is public to allow it to be called off-chain.
Expand Down
49 changes: 48 additions & 1 deletion pkg/pool-hooks/test/foundry/MevCaptureHook.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ pragma solidity ^0.8.24;
import "forge-std/Test.sol";

import { IAuthentication } from "@balancer-labs/v3-interfaces/contracts/solidity-utils/helpers/IAuthentication.sol";
import { PoolSwapParams, MAX_FEE_PERCENTAGE } from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";
import {
PoolSwapParams,
MAX_FEE_PERCENTAGE,
PoolRoleAccounts
} from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";
import { IVaultExtension } from "@balancer-labs/v3-interfaces/contracts/vault/IVaultExtension.sol";
import { IMevCaptureHook } from "@balancer-labs/v3-interfaces/contracts/pool-hooks/IMevCaptureHook.sol";
import { IHooks } from "@balancer-labs/v3-interfaces/contracts/vault/IHooks.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";
Expand Down Expand Up @@ -417,6 +422,19 @@ contract MevCaptureHookTest is BaseVaultTest {
);
}

function testSetPoolMevTaxMultiplierRevertIfSenderIsNotFeeManager() public {
_mockPoolRoleAccounts(address(0x01));

vm.expectRevert(IAuthentication.SenderNotAllowed.selector);
_mevCaptureHook.setPoolMevTaxMultiplier(pool, 5e18);
}

function testSetPoolMevTaxMultiplierWithSwapFeeManager() public {
_mockPoolRoleAccounts(address(this));

_mevCaptureHook.setPoolMevTaxMultiplier(pool, 5e18);
}

/********************************************************
getPoolMevTaxThreshold()
********************************************************/
Expand Down Expand Up @@ -450,6 +468,19 @@ contract MevCaptureHookTest is BaseVaultTest {
_mevCaptureHook.setPoolMevTaxThreshold(pool, 5e18);
}

function testSetPoolMevTaxThresholdRevertIfSenderIsNotFeeManager() public {
_mockPoolRoleAccounts(address(0x01));

vm.expectRevert(IAuthentication.SenderNotAllowed.selector);
_mevCaptureHook.setPoolMevTaxThreshold(pool, 5e18);
}

function testSetPoolMevTaxThresholdWithSwapFeeManager() public {
_mockPoolRoleAccounts(address(this));

_mevCaptureHook.setPoolMevTaxThreshold(pool, 5e18);
}

function testSetPoolMevTaxThreshold() public {
uint256 firstPoolMevTaxThreshold = _mevCaptureHook.getPoolMevTaxThreshold(pool);
uint256 firstDefaultMevTaxThreshold = _mevCaptureHook.getDefaultMevTaxThreshold();
Expand Down Expand Up @@ -731,4 +762,20 @@ contract MevCaptureHookTest is BaseVaultTest {
assertFalse(_mevCaptureHook.isMevTaxExemptSender(bob), "Bob is exempt");
assertTrue(_mevCaptureHook.isMevTaxExemptSender(alice), "Alice is not exempt");
}

/********************************************************
Other
********************************************************/
function _mockPoolRoleAccounts(address swapFeeManager) private {
PoolRoleAccounts memory poolRoleAccounts = PoolRoleAccounts({
pauseManager: address(0x01),
swapFeeManager: swapFeeManager,
poolCreator: address(0x01)
});
vm.mockCall(
address(vault),
abi.encodeWithSelector(IVaultExtension.getPoolRoleAccounts.selector, pool),
abi.encode(poolRoleAccounts)
);
}
}
18 changes: 18 additions & 0 deletions pkg/vault/contracts/SingletonAuthentication.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import { Authentication } from "@balancer-labs/v3-solidity-utils/contracts/helpe
abstract contract SingletonAuthentication is Authentication {
IVault private immutable _vault;

modifier onlySwapFeeManagerOrGovernance(address pool) {
address roleAddress = _vault.getPoolRoleAccounts(pool).swapFeeManager;
_ensureAuthenticatedByExclusiveRole(pool, roleAddress);
_;
}

// Use the contract's own address to disambiguate action identifiers.
constructor(IVault vault) Authentication(bytes32(uint256(uint160(address(this))))) {
_vault = vault;
Expand Down Expand Up @@ -44,4 +50,16 @@ abstract contract SingletonAuthentication is Authentication {
function _canPerform(bytes32 actionId, address account, address where) internal view returns (bool) {
return getAuthorizer().canPerform(actionId, account, where);
}

/// @dev Ensure the sender is the roleAddress, or default to governance if roleAddress is address(0).
function _ensureAuthenticatedByExclusiveRole(address pool, address roleAddress) internal view {
if (roleAddress == address(0)) {
// Defer to governance if no role assigned.
if (_canPerform(getActionId(msg.sig), msg.sender, pool) == false) {
revert SenderNotAllowed();
}
} else if (msg.sender != roleAddress) {
revert SenderNotAllowed();
}
}
}

0 comments on commit 01c1a9b

Please sign in to comment.