diff --git a/pkg/vault/contracts/test/MevRouterMock.sol b/pkg/vault/contracts/test/MevRouterMock.sol new file mode 100644 index 000000000..a3c5eaf68 --- /dev/null +++ b/pkg/vault/contracts/test/MevRouterMock.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity ^0.8.24; + +import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol"; +import { IPermit2 } from "permit2/src/interfaces/IPermit2.sol"; + +import { IWETH } from "@balancer-labs/v3-interfaces/contracts/solidity-utils/misc/IWETH.sol"; +import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol"; + +import { MevRouter } from "../MevRouter.sol"; + +string constant MOCK_MEV_ROUTER_VERSION = "Mock Router v1"; + +contract MevRouterMock is MevRouter { + error MockErrorCode(); + + constructor( + IVault vault, + IWETH weth, + IPermit2 permit2, + MevRouterParams memory params + ) MevRouter(vault, weth, permit2, MOCK_MEV_ROUTER_VERSION, params) { + // solhint-disable-previous-line no-empty-blocks + } +} diff --git a/pkg/vault/contracts/test/MevTaxCollectorMock.sol b/pkg/vault/contracts/test/MevTaxCollectorMock.sol new file mode 100644 index 000000000..2c87f02e3 --- /dev/null +++ b/pkg/vault/contracts/test/MevTaxCollectorMock.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +pragma solidity ^0.8.24; + +import { IMevTaxCollector } from "@balancer-labs/v3-interfaces/contracts/vault/IMevTaxCollector.sol"; + +contract MevTaxCollectorMock is IMevTaxCollector { + function chargeMevTax(address pool) external payable { + return; + } +} diff --git a/pkg/vault/test/foundry/MevRouter.t.sol b/pkg/vault/test/foundry/MevRouter.t.sol new file mode 100644 index 000000000..b3b98ffd3 --- /dev/null +++ b/pkg/vault/test/foundry/MevRouter.t.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; + +import { IAuthentication } from "@balancer-labs/v3-interfaces/contracts/solidity-utils/helpers/IAuthentication.sol"; +import { IMevRouter } from "@balancer-labs/v3-interfaces/contracts/vault/IMevRouter.sol"; + +import { BaseVaultTest } from "./utils/BaseVaultTest.sol"; + +contract MevRouterTest is BaseVaultTest { + function setUp() public override { + super.setUp(); + + authorizer.grantRole(mevRouter.getActionId(IMevRouter.disableMevTax.selector), admin); + authorizer.grantRole(mevRouter.getActionId(IMevRouter.enableMevTax.selector), admin); + } + + function testDisableMevTax() public { + assertTrue(mevRouter.isMevTaxEnabled(), "Mev Tax is not enabled"); + vm.prank(admin); + mevRouter.disableMevTax(); + assertFalse(mevRouter.isMevTaxEnabled(), "Mev Tax is not disabled"); + } + + function testDisableMevTaxIsAuthenticated() public { + vm.expectRevert(IAuthentication.SenderNotAllowed.selector); + mevRouter.disableMevTax(); + } + + function testEnableMevTax() public { + vm.prank(admin); + mevRouter.disableMevTax(); + assertFalse(mevRouter.isMevTaxEnabled(), "Mev Tax is not disabled"); + vm.prank(admin); + mevRouter.enableMevTax(); + assertTrue(mevRouter.isMevTaxEnabled(), "Mev Tax is not enabled"); + } + + function testEnableMevTaxIsAuthenticated() public { + vm.expectRevert(IAuthentication.SenderNotAllowed.selector); + mevRouter.enableMevTax(); + } +} diff --git a/pkg/vault/test/foundry/utils/BaseVaultTest.sol b/pkg/vault/test/foundry/utils/BaseVaultTest.sol index 2497ac61e..249921787 100644 --- a/pkg/vault/test/foundry/utils/BaseVaultTest.sol +++ b/pkg/vault/test/foundry/utils/BaseVaultTest.sol @@ -28,6 +28,7 @@ import { PoolFactoryMock } from "../../../contracts/test/PoolFactoryMock.sol"; import { PoolHooksMock } from "../../../contracts/test/PoolHooksMock.sol"; import { RouterMock } from "../../../contracts/test/RouterMock.sol"; import { BufferRouterMock } from "../../../contracts/test/BufferRouterMock.sol"; +import { MevRouterMock } from "../../../contracts/test/MevRouterMock.sol"; import { VaultStorage } from "../../../contracts/VaultStorage.sol"; import { PoolMock } from "../../../contracts/test/PoolMock.sol"; @@ -84,6 +85,7 @@ abstract contract BaseVaultTest is VaultContractsDeployer, VaultStorage, BaseTes RouterMock internal router; BatchRouterMock internal batchRouter; BufferRouterMock internal bufferRouter; + MevRouterMock internal mevRouter; PoolFactoryMock internal factoryMock; RateProviderMock internal rateProvider; CompositeLiquidityRouterMock internal compositeLiquidityRouter; @@ -160,6 +162,8 @@ abstract contract BaseVaultTest is VaultContractsDeployer, VaultStorage, BaseTes vm.label(address(compositeLiquidityRouter), "composite liquidity router"); bufferRouter = deployBufferRouterMock(IVault(address(vault)), weth, permit2); vm.label(address(bufferRouter), "buffer router"); + mevRouter = deployMevRouterMock(IVault(address(vault)), weth, permit2); + vm.label(address(mevRouter), "mev router"); feeController = vault.getProtocolFeeController(); vm.label(address(feeController), "fee controller"); @@ -184,6 +188,7 @@ abstract contract BaseVaultTest is VaultContractsDeployer, VaultStorage, BaseTes permit2.approve(address(tokens[i]), address(router), type(uint160).max, type(uint48).max); permit2.approve(address(tokens[i]), address(bufferRouter), type(uint160).max, type(uint48).max); permit2.approve(address(tokens[i]), address(batchRouter), type(uint160).max, type(uint48).max); + permit2.approve(address(tokens[i]), address(mevRouter), type(uint160).max, type(uint48).max); permit2.approve(address(tokens[i]), address(compositeLiquidityRouter), type(uint160).max, type(uint48).max); } @@ -192,6 +197,7 @@ abstract contract BaseVaultTest is VaultContractsDeployer, VaultStorage, BaseTes permit2.approve(address(erc4626Tokens[i]), address(router), type(uint160).max, type(uint48).max); permit2.approve(address(erc4626Tokens[i]), address(bufferRouter), type(uint160).max, type(uint48).max); permit2.approve(address(erc4626Tokens[i]), address(batchRouter), type(uint160).max, type(uint48).max); + permit2.approve(address(erc4626Tokens[i]), address(mevRouter), type(uint160).max, type(uint48).max); permit2.approve( address(erc4626Tokens[i]), address(compositeLiquidityRouter), @@ -212,12 +218,14 @@ abstract contract BaseVaultTest is VaultContractsDeployer, VaultStorage, BaseTes bpt.approve(address(router), type(uint256).max); bpt.approve(address(bufferRouter), type(uint256).max); bpt.approve(address(batchRouter), type(uint256).max); + bpt.approve(address(mevRouter), type(uint256).max); bpt.approve(address(compositeLiquidityRouter), type(uint256).max); IERC20(bpt).approve(address(permit2), type(uint256).max); permit2.approve(address(bpt), address(router), type(uint160).max, type(uint48).max); permit2.approve(address(bpt), address(bufferRouter), type(uint160).max, type(uint48).max); permit2.approve(address(bpt), address(batchRouter), type(uint160).max, type(uint48).max); + permit2.approve(address(bpt), address(mevRouter), type(uint160).max, type(uint48).max); permit2.approve(address(bpt), address(compositeLiquidityRouter), type(uint160).max, type(uint48).max); vm.stopPrank(); diff --git a/pkg/vault/test/foundry/utils/VaultContractsDeployer.sol b/pkg/vault/test/foundry/utils/VaultContractsDeployer.sol index 264ad2eff..5de4eb2a3 100644 --- a/pkg/vault/test/foundry/utils/VaultContractsDeployer.sol +++ b/pkg/vault/test/foundry/utils/VaultContractsDeployer.sol @@ -10,6 +10,8 @@ import { IPermit2 } from "permit2/src/interfaces/IPermit2.sol"; import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol"; import { IAuthorizer } from "@balancer-labs/v3-interfaces/contracts/vault/IAuthorizer.sol"; import { IVaultMock } from "@balancer-labs/v3-interfaces/contracts/test/IVaultMock.sol"; +import { IMevRouter } from "@balancer-labs/v3-interfaces/contracts/vault/IMevRouter.sol"; +import { IMevTaxCollector } from "@balancer-labs/v3-interfaces/contracts/vault/IMevTaxCollector.sol"; import { IWETH } from "@balancer-labs/v3-interfaces/contracts/solidity-utils/misc/IWETH.sol"; import { HooksConfigLibMock } from "@balancer-labs/v3-vault/contracts/test/HooksConfigLibMock.sol"; import { BaseContractsDeployer } from "@balancer-labs/v3-solidity-utils/test/foundry/utils/BaseContractsDeployer.sol"; @@ -35,6 +37,8 @@ import { RateProviderMock } from "../../../contracts/test/RateProviderMock.sol"; import { RouterCommonMock } from "../../../contracts/test/RouterCommonMock.sol"; import { RouterMock } from "../../../contracts/test/RouterMock.sol"; import { BufferRouterMock } from "../../../contracts/test/BufferRouterMock.sol"; +import { MevRouterMock } from "../../../contracts/test/MevRouterMock.sol"; +import { MevTaxCollectorMock } from "../../../contracts/test/MevTaxCollectorMock.sol"; /** * @notice This contract contains functions for deploying mocks and contracts related to the "Vault". @@ -287,6 +291,30 @@ contract VaultContractsDeployer is BaseContractsDeployer { } } + function deployMevRouterMock(IVault vault, IWETH weth, IPermit2 permit2) internal returns (MevRouterMock) { + IMevTaxCollector collector = new MevTaxCollectorMock(); + + IMevRouter.MevRouterParams memory params = IMevRouter.MevRouterParams({ + mevTaxCollector: collector, + mevTaxMultiplier: 9, + priorityGasThreshold: 1e18 + }); + + if (reusingArtifacts) { + return + MevRouterMock( + payable( + deployCode( + _computeVaultTestPath(type(MevRouterMock).name), + abi.encode(vault, weth, permit2, params) + ) + ) + ); + } else { + return new MevRouterMock(vault, weth, permit2, params); + } + } + function _computeVaultPath(string memory name) private view returns (string memory) { return string(abi.encodePacked(artifactsRootDir, "contracts/", name, ".sol/", name, ".json")); }