Skip to content

Commit 6bf9c6b

Browse files
committed
simplify protocol fees
1 parent 019b041 commit 6bf9c6b

21 files changed

+297
-214
lines changed

remappings.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@ ethereum-vault-connector/=lib/ethereum-vault-connector/src/
55
evk-test/=lib/euler-vault-kit/test/
66
permit2/=lib/euler-vault-kit/lib/permit2/
77
@uniswap/v4-core/=lib/v4-periphery/lib/v4-core/
8-
solmate/=lib/v4-periphery/lib/v4-core/lib/solmate/src

script/DeployProtocol.s.sol

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {EulerSwapFactory} from "../src/EulerSwapFactory.sol";
66
import {EulerSwapRegistry} from "../src/EulerSwapRegistry.sol";
77
import {EulerSwapPeriphery} from "../src/EulerSwapPeriphery.sol";
88
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
9+
import {EulerSwapProtocolFeeConfig} from "../src/EulerSwapProtocolFeeConfig.sol";
910
import {EulerSwap} from "../src/EulerSwap.sol";
1011
import {EulerSwapManagement} from "../src/EulerSwapManagement.sol";
1112

@@ -22,16 +23,17 @@ contract DeployProtocol is ScriptUtil {
2223

2324
address evc = vm.parseJsonAddress(json, ".evc");
2425
address poolManager = vm.parseJsonAddress(json, ".poolManager");
25-
address feeOwner = vm.parseJsonAddress(json, ".feeOwner");
26-
address feeRecipientSetter = vm.parseJsonAddress(json, ".feeRecipientSetter");
26+
address protocolFeeAdmin = vm.parseJsonAddress(json, ".protocolFeeAdmin");
2727
address validVaultPerspective = vm.parseJsonAddress(json, ".validVaultPerspective");
2828
address curator = vm.parseJsonAddress(json, ".curator");
2929

3030
vm.startBroadcast(deployerAddress);
3131

32+
address eulerSwapProtocolFeeConfig = address(new EulerSwapProtocolFeeConfig(evc, protocolFeeAdmin));
3233
address eulerSwapManagementImpl = address(new EulerSwapManagement(evc));
33-
address eulerSwapImpl = address(new EulerSwap(evc, poolManager, eulerSwapManagementImpl));
34-
address eulerSwapFactory = address(new EulerSwapFactory(evc, eulerSwapImpl, feeOwner, feeRecipientSetter));
34+
address eulerSwapImpl =
35+
address(new EulerSwap(evc, eulerSwapProtocolFeeConfig, poolManager, eulerSwapManagementImpl));
36+
address eulerSwapFactory = address(new EulerSwapFactory(evc, eulerSwapImpl));
3537
new EulerSwapRegistry(evc, eulerSwapFactory, validVaultPerspective, curator);
3638
new EulerSwapPeriphery();
3739
vm.stopBroadcast();
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
{
22
"evc": "0x5301c7dD20bD945D2013b48ed0DEE3A284ca8989",
33
"poolManager": "0x498581fF718922c3f8e6A244956aF099B2652b2b",
4-
"feeOwner": "0x33C71422B3E20ef2472Bc9aa9252220CAeAF207e",
5-
"feeRecipientSetter": "0x33C71422B3E20ef2472Bc9aa9252220CAeAF207e",
4+
"protocolFeeAdmin": "0x33C71422B3E20ef2472Bc9aa9252220CAeAF207e",
65
"validVaultPerspective": "0xFEA8e8a4d7ab8C517c3790E49E92ED7E1166F651",
76
"curator": "0x33C71422B3E20ef2472Bc9aa9252220CAeAF207e"
87
}

src/EulerSwap.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ contract EulerSwap is IEulerSwap, UniswapHook {
2323
/// @notice Emitted upon EulerSwap instance creation or reconfiguration.
2424
event EulerSwapManagerSet(address indexed manager, bool installed);
2525

26-
constructor(address evc_, address poolManager_, address managementImpl_) UniswapHook(evc_, poolManager_) {
26+
constructor(address evc_, address protocolFeeConfig_, address poolManager_, address managementImpl_)
27+
UniswapHook(evc_, protocolFeeConfig_, poolManager_)
28+
{
2729
managementImpl = managementImpl_;
2830
}
2931

@@ -140,7 +142,7 @@ contract EulerSwap is IEulerSwap, UniswapHook {
140142

141143
// Setup context
142144

143-
SwapLib.SwapContext memory ctx = SwapLib.init(address(evc), _msgSender(), to);
145+
SwapLib.SwapContext memory ctx = SwapLib.init(address(evc), protocolFeeConfig, _msgSender(), to);
144146
SwapLib.setAmountsOut(ctx, amount0Out, amount1Out);
145147
SwapLib.invokeBeforeSwapHook(ctx);
146148

src/EulerSwapFactory.sol

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ import {IEulerSwapFactory, IEulerSwap} from "./interfaces/IEulerSwapFactory.sol"
55
import {EVCUtil} from "ethereum-vault-connector/utils/EVCUtil.sol";
66

77
import {EulerSwap} from "./EulerSwap.sol";
8-
import {ProtocolFee} from "./utils/ProtocolFee.sol";
98
import {MetaProxyDeployer} from "./utils/MetaProxyDeployer.sol";
109

1110
/// @title EulerSwapFactory contract
1211
/// @custom:security-contact [email protected]
1312
/// @author Euler Labs (https://www.eulerlabs.com/)
14-
contract EulerSwapFactory is IEulerSwapFactory, EVCUtil, ProtocolFee {
13+
contract EulerSwapFactory is IEulerSwapFactory, EVCUtil {
1514
/// @dev The EulerSwap code instance that will be proxied to
1615
address public immutable eulerSwapImpl;
1716

@@ -20,12 +19,8 @@ contract EulerSwapFactory is IEulerSwapFactory, EVCUtil, ProtocolFee {
2019

2120
error Unauthorized();
2221
error OperatorNotInstalled();
23-
error InvalidProtocolFee();
2422

25-
constructor(address evc, address eulerSwapImpl_, address feeOwner_, address feeRecipientSetter_)
26-
EVCUtil(evc)
27-
ProtocolFee(feeOwner_, feeRecipientSetter_)
28-
{
23+
constructor(address evc, address eulerSwapImpl_) EVCUtil(evc) {
2924
eulerSwapImpl = eulerSwapImpl_;
3025
}
3126

@@ -37,10 +32,6 @@ contract EulerSwapFactory is IEulerSwapFactory, EVCUtil, ProtocolFee {
3732
bytes32 salt
3833
) external returns (address) {
3934
require(_msgSender() == sParams.eulerAccount, Unauthorized());
40-
require(
41-
sParams.protocolFee == protocolFee && sParams.protocolFeeRecipient == protocolFeeRecipient,
42-
InvalidProtocolFee()
43-
);
4435

4536
EulerSwap pool = EulerSwap(MetaProxyDeployer.deployMetaProxy(eulerSwapImpl, abi.encode(sParams), salt));
4637
deployedPools[address(pool)] = true;
@@ -67,9 +58,4 @@ contract EulerSwapFactory is IEulerSwapFactory, EVCUtil, ProtocolFee {
6758
)
6859
);
6960
}
70-
71-
/// @dev For ProtocolFee access
72-
function _eulerSwapImpl() internal view override returns (address) {
73-
return eulerSwapImpl;
74-
}
7561
}

src/EulerSwapProtocolFeeConfig.sol

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.27;
3+
4+
import {IEulerSwapProtocolFeeConfig} from "./interfaces/IEulerSwapProtocolFeeConfig.sol";
5+
import {EVCUtil} from "evc/utils/EVCUtil.sol";
6+
7+
/// @title EulerSwapProtocolFeeConfig contract
8+
/// @custom:security-contact [email protected]
9+
/// @author Euler Labs (https://www.eulerlabs.com/)
10+
contract EulerSwapProtocolFeeConfig is IEulerSwapProtocolFeeConfig, EVCUtil {
11+
/// @dev Protocol fee admin
12+
address public admin;
13+
14+
/// @dev Admin is not allowed to set a protocol fee larger than this
15+
uint64 public constant MAX_PROTOCOL_FEE = 0.15e18;
16+
17+
/// @dev Destination of collected protocol fees, unless overridden
18+
address public defaultRecipient;
19+
/// @dev Default protocol fee, 1e18-scale
20+
uint64 public defaultFee;
21+
22+
struct Override {
23+
bool exists;
24+
address recipient;
25+
uint64 fee;
26+
}
27+
28+
/// @dev EulerSwap-instance specific fee override
29+
mapping(address pool => Override) public overrides;
30+
31+
error Unauthorized();
32+
error InvalidProtocolFee();
33+
34+
constructor(address evc, address admin_) EVCUtil(evc) {
35+
admin = admin_;
36+
}
37+
38+
modifier onlyAdmin() {
39+
// Ensures that the caller is not an operator, controller, etc
40+
_authenticateCallerWithStandardContextState(true);
41+
42+
require(_msgSender() == admin, Unauthorized());
43+
44+
_;
45+
}
46+
47+
/// @inheritdoc IEulerSwapProtocolFeeConfig
48+
function setAdmin(address newAdmin) external onlyAdmin {
49+
admin = newAdmin;
50+
}
51+
52+
/// @inheritdoc IEulerSwapProtocolFeeConfig
53+
function setDefault(address recipient, uint64 fee) external onlyAdmin {
54+
require(fee <= MAX_PROTOCOL_FEE, InvalidProtocolFee());
55+
56+
defaultRecipient = recipient;
57+
defaultFee = fee;
58+
}
59+
60+
/// @inheritdoc IEulerSwapProtocolFeeConfig
61+
function setOverride(address pool, address recipient, uint64 fee) external onlyAdmin {
62+
require(fee <= MAX_PROTOCOL_FEE, InvalidProtocolFee());
63+
64+
overrides[pool] = Override({exists: true, recipient: recipient, fee: fee});
65+
}
66+
67+
/// @inheritdoc IEulerSwapProtocolFeeConfig
68+
function removeOverride(address pool) external onlyAdmin {
69+
delete overrides[pool];
70+
}
71+
72+
/// @inheritdoc IEulerSwapProtocolFeeConfig
73+
function getProtocolFee(address pool) external view returns (address recipient, uint64 fee) {
74+
Override memory o = overrides[pool];
75+
76+
if (o.exists) {
77+
recipient = o.recipient;
78+
fee = o.fee;
79+
80+
if (recipient == address(0)) recipient = defaultRecipient;
81+
} else {
82+
recipient = defaultRecipient;
83+
fee = defaultFee;
84+
}
85+
}
86+
}

src/UniswapHook.sol

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,16 @@ import {SwapLib} from "./libraries/SwapLib.sol";
2525
abstract contract UniswapHook is EulerSwapBase, BaseHook {
2626
using SafeCast for uint256;
2727

28+
address public immutable protocolFeeConfig;
29+
2830
PoolKey internal _poolKey;
2931

30-
constructor(address evc_, address _poolManager) EulerSwapBase(evc_) BaseHook(IPoolManager(_poolManager)) {}
32+
constructor(address evc_, address protocolFeeConfig_, address _poolManager)
33+
EulerSwapBase(evc_)
34+
BaseHook(IPoolManager(_poolManager))
35+
{
36+
protocolFeeConfig = protocolFeeConfig_;
37+
}
3138

3239
function activateHook(IEulerSwap.StaticParams memory sParams) internal nonReentrant {
3340
if (address(poolManager) == address(0)) return;
@@ -65,7 +72,7 @@ abstract contract UniswapHook is EulerSwapBase, BaseHook {
6572
nonReentrant
6673
returns (bytes4, BeforeSwapDelta, uint24)
6774
{
68-
SwapLib.SwapContext memory ctx = SwapLib.init(address(evc), sender, msg.sender);
75+
SwapLib.SwapContext memory ctx = SwapLib.init(address(evc), protocolFeeConfig, sender, msg.sender);
6976

7077
uint256 amountIn;
7178
uint256 amountOut;

src/interfaces/IEulerSwap.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ interface IEulerSwap {
1010
address borrowVault1;
1111
address eulerAccount;
1212
address feeRecipient;
13-
address protocolFeeRecipient;
14-
uint64 protocolFee;
1513
}
1614

1715
/// @dev Reconfigurable pool parameters, loaded from storage.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity >=0.8.0;
3+
4+
interface IEulerSwapProtocolFeeConfig {
5+
/// @notice Change the admin
6+
/// @param newAdmin New admin address (can be address(0) to renounce)
7+
function setAdmin(address newAdmin) external;
8+
9+
/// @notice Update the default protocol fee settings
10+
/// @param recipient Address to receive the protocol fees
11+
/// @param fee Proportion of LP fees claimed as protocol fees (1e18 scale). Must be <= 15%.
12+
function setDefault(address recipient, uint64 fee) external;
13+
14+
/// @notice Override a particular pool's protocol fee settings
15+
/// @param pool The EulerSwap instance to override
16+
/// @param recipient Address to receive the protocol fees. If address(0), then use the default recipient.
17+
/// @param fee Proportion of LP fees claimed as protocol fees (1e18 scale). Must be <= 15%.
18+
function setOverride(address pool, address recipient, uint64 fee) external;
19+
20+
/// @notice Removes an override for a particular pool
21+
/// @param pool The EulerSwap instance
22+
function removeOverride(address pool) external;
23+
24+
/// @notice Retrieve protocol fee configuration for a given pool
25+
/// @param pool Which pool to read the config for
26+
/// @return recipient Address to receive the protocol fees
27+
/// @return fee Proportion of LP fees claimed as protocol fees (1e18 scale)
28+
function getProtocolFee(address pool) external view returns (address recipient, uint64 fee);
29+
}

src/libraries/CtxLib.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@ library CtxLib {
6060

6161
/// @dev Unpacks encoded Params from trailing calldata. Loosely based on
6262
/// the implementation from EIP-3448 (except length is hard-coded).
63-
/// 256 is the size of the StaticParams struct after ABI encoding.
63+
/// 192 is the size of the StaticParams struct after ABI encoding.
6464
function getStaticParams() internal pure returns (IEulerSwap.StaticParams memory p) {
65-
require(msg.data.length >= 256, InsufficientCalldata());
65+
require(msg.data.length >= 192, InsufficientCalldata());
6666
unchecked {
67-
return abi.decode(msg.data[msg.data.length - 256:], (IEulerSwap.StaticParams));
67+
return abi.decode(msg.data[msg.data.length - 192:], (IEulerSwap.StaticParams));
6868
}
6969
}
7070
}

0 commit comments

Comments
 (0)