Skip to content

Commit 5e8dc25

Browse files
Merge pull request #286 from euler-xyz/development
Development
2 parents b6511f9 + 071178d commit 5e8dc25

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1507
-397
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@
3434
[submodule "lib/solidity-bytes-utils"]
3535
path = lib/solidity-bytes-utils
3636
url = https://github.com/GNSPS/solidity-bytes-utils
37+
[submodule "lib/euler-swap"]
38+
path = lib/euler-swap
39+
url = https://github.com/euler-xyz/euler-swap

docs/governor-access-control-emergency-factory.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,8 @@ This factory builds upon the selector-based access control system described in t
133133

134134
The `GovernorAccessControlEmergencyFactory` provides a streamlined way to deploy a complete governance system with timelocks and emergency response capabilities. This approach balances the need for careful governance with the ability to respond rapidly to emergencies, making it well-suited for managing risk parameters of the EVK vaults.
135135

136-
By enforcing time delays through timelocks, the system prioritizes user protection, giving vault users the transparency and time needed to assess and respond to governance changes. At the same time, the dual timelock architecture provides both operational flexibility and security guarantees, while the standardized factory deployment makes it easier to implement consistent monitoring across all governance instances, enhancing the security of the entire ecosystem.
136+
By enforcing time delays through timelocks, the system prioritizes user protection, giving vault users the transparency and time needed to assess and respond to governance changes. At the same time, the dual timelock architecture provides both operational flexibility and security guarantees, while the standardized factory deployment makes it easier to implement consistent monitoring across all governance instances, enhancing the security of the entire ecosystem.
137+
138+
## Governor Contracts Suite
139+
140+
![Governor Contracts Suite](./governor_contracts_suite.png)

docs/governor_contracts_suite.png

177 KB
Loading

docs/hook-target-access-control-keyring.md

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Overview
44

5-
The `HookTargetAccessControlKeyring` is a specialized hook target contract that combines selector-based access control with keyring credential verification. This contract enables vault operators to restrict access to critical vault operations, ensuring only authorized users with valid credentials can perform operations on vaults.
5+
The `HookTargetAccessControlKeyring` is a specialized hook target contract that combines selector-based access control with Keyring credential verification. This contract enables vault operators to restrict access to critical vault operations, ensuring only authorized users with valid credentials can perform operations on vaults.
66

77
## Purpose
88

@@ -30,23 +30,29 @@ The contract implements a sophisticated authentication workflow through the `_au
3030

3131
1. **Initial Role Check**:
3232
- First, check if the caller has either the `WILD_CARD` role or a specific role for the current function selector
33-
- If yes, bypass the keyring credential check entirely
33+
- If yes, bypass the Keyring credential check entirely
3434

3535
2. **Caller Credential Check**:
3636
- Determine the EVC owner of the calling account
3737
- If no owner is registered, assume the caller itself is the owner
38-
- Verify the owner has valid credentials according to the specified policy ID
38+
- If the owner lacks valid Keyring credential according to the policy ID:
39+
- Check if they have the `PRIVILEGED_ACCOUNT_ROLE` AND they don't share a common owner with the account being operated on
40+
- If neither credential exists nor the privileged role condition is met, revert with `NotAuthorized`
3941

4042
3. **Account Credential Check**:
4143
- Only performed if the account being operated on has a different EVC owner than the caller
4244
- Determine the EVC owner of the account being operated on
4345
- If no owner is registered, assume the account itself is the owner
44-
- Verify this owner also has valid credentials according to the policy ID
46+
- If the owner lacks valid Keyring credential according to the policy ID:
47+
- Check if they have the `PRIVILEGED_ACCOUNT_ROLE`
48+
- If neither credential nor privileged role exists, revert with `NotAuthorized`
4549

46-
4. **Optimization**:
47-
- If the caller and the account being operated on share the same EVC owner, only one credential check is performed
50+
4. **Privileged Account Restrictions**:
51+
- When a user tries to operate on an account that shares the same EVC owner, they cannot use their privileged status to bypass credential checks
52+
- This means that privileged accounts must have valid credentials to perform operations on their own accounts
53+
- However, privileged accounts can bypass credential checks when operating on accounts that belong to a different EVC owner
4854

49-
This mechanism ensures that both the entity initiating the operation and the entity being affected by it (if different) have proper authorization.
55+
This mechanism ensures that both the entity initiating the operation and the entity being affected by it (if different) have proper authorization, while maintaining strict controls on privileged account usage.
5056

5157
## Hooked Operations
5258

@@ -76,7 +82,7 @@ The contract implements a fallback function that provides a catch-all authentica
7682
When a hooked operation is called that doesn't match any of the explicitly defined functions, the fallback function:
7783
- Authenticates only the caller using the `_authenticateCaller` function
7884
- Applies the same role-based access control rules (WILD_CARD and selector-specific roles)
79-
- Does not perform keyring credential checks on any additional accounts
85+
- Does not perform Keyring credential checks on any additional accounts
8086

8187
## Usage Patterns
8288

@@ -100,7 +106,15 @@ To use this hook target:
100106
A vault operator could use this hook target to create a vault cluster where:
101107
- Only users with valid credentials can perform operations on the vaults
102108
- Specific addresses (like chosen liquidators) are whitelisted to bypass credential checks
109+
- The Swapper and SwapVerifier contracts are granted the PRIVILEGED_ACCOUNT_ROLE to enable asset withdrawals for swaps while preventing self-deposits
103110

104111
To achieve the above, the following operations should be hooked: `OP_DEPOSIT`, `OP_MINT`, `OP_WITHDRAW`, `OP_REDEEM`, `OP_SKIM`, `OP_BORROW`, `OP_REPAY`, `OP_REPAY_WITH_SHARES`, `OP_PULL_DEBT`, `OP_LIQUIDATE` and `OP_FLASHLOAN`.
105112

106-
The liquidators should be granted the `WILD_CARD` role so that they can liquidate and close the position without needing to obtain a Keyring credential.
113+
The liquidators should be granted the `WILD_CARD` role so that they can liquidate and close the position without needing to obtain a Keyring credential.
114+
115+
The Swapper and the SwapVerifier contracts should be granted the `PRIVILEGED_ACCOUNT_ROLE` to enable them to:
116+
- Receive the assets withdrawn from user accounts for swapping
117+
- Deposit/skim swapped assets back into user accounts
118+
- Repay debt on behalf of users
119+
120+
However, the Swapper cannot deposit assets into its own account since the `_authenticateCallerAndAccount` function prevents privileged accounts from performing operations on accounts that share their EVC owner. This means that while the Swapper can operate on user accounts (which have different EVC owners), it must have valid credentials to perform operations on its own account.

foundry.toml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,8 @@ number_underscore = "preserve"
2525
override_spacing = true
2626
wrap_comments = true
2727
ignore = [
28-
"script/production/mainnet/clusters/PrimeCluster.s.sol",
29-
"script/production/mainnet/clusters/YieldCluster.s.sol",
30-
"script/production/mainnet/clusters/RepoCluster.s.sol",
31-
"script/production/base/clusters/BaseCluster.s.sol",
32-
"script/production/swell/clusters/SwellCluster.s.sol",
33-
"script/production/swell/clusters/SwellETHCluster.s.sol"
28+
"script/production/*/*",
29+
"script/production/*/*/*"
3430
]
3531

3632
[profile.default.fuzz]

lib/euler-swap

Submodule euler-swap added at b948f40

remappings.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ lib/layerzero-devtools/packages/oapp-evm-upgradeable/contracts:@openzeppelin/con
1313
@layerzerolabs/lz-evm-oapp-v2/=lib/layerzero-v2/packages/layerzero-v2/evm/oapp
1414
openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/
1515
openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
16+
lib/fee-flow/src:solmate/=lib/fee-flow/lib/solmate/src
17+
lib/fee-flow/utils:solmate/=lib/fee-flow/lib/solmate/utils
18+
lib/fee-flow/tokens:solmate/=lib/fee-flow/lib/solmate/tokens
19+
lib/euler-swap:solmate/=lib/euler-swap/lib/euler-vault-kit/lib/permit2/lib/solmate
1620
ethereum-vault-connector/=lib/ethereum-vault-connector/src/
1721
evc/=lib/ethereum-vault-connector/src/
1822
evk/=lib/euler-vault-kit/src/
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
pragma solidity ^0.8.0;
4+
5+
import {ScriptUtils} from "./utils/ScriptUtils.s.sol";
6+
7+
contract EulerSwapImplementation is ScriptUtils {
8+
function run() public broadcast returns (address implementation) {
9+
string memory inputScriptFileName = "22_EulerSwapImplementation_input.json";
10+
string memory outputScriptFileName = "22_EulerSwapImplementation_output.json";
11+
string memory json = getScriptFile(inputScriptFileName);
12+
address evc = vm.parseJsonAddress(json, ".evc");
13+
address poolManager = vm.parseJsonAddress(json, ".poolManager");
14+
15+
implementation = execute(evc, poolManager);
16+
17+
string memory object;
18+
object = vm.serializeAddress("implementation", "eulerSwapImplementation", implementation);
19+
20+
vm.writeJson(object, string.concat(vm.projectRoot(), "/script/", outputScriptFileName));
21+
}
22+
23+
function deploy(address evc, address poolManager) public broadcast returns (address implementation) {
24+
implementation = execute(evc, poolManager);
25+
}
26+
27+
function execute(address evc, address poolManager) public returns (address implementation) {
28+
bytes memory bytecode =
29+
abi.encodePacked(vm.getCode("out-euler-swap/EulerSwap.sol/EulerSwap.json"), abi.encode(evc, poolManager));
30+
assembly {
31+
implementation := create(0, add(bytecode, 0x20), mload(bytecode))
32+
}
33+
}
34+
}

script/23_EulerSwapFactory.s.sol

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
pragma solidity ^0.8.0;
4+
5+
import {ScriptUtils} from "./utils/ScriptUtils.s.sol";
6+
7+
contract EulerSwapFactory is ScriptUtils {
8+
function run() public broadcast returns (address eulerSwapFactory) {
9+
string memory inputScriptFileName = "23_EulerSwapFactory_input.json";
10+
string memory outputScriptFileName = "23_EulerSwapFactory_output.json";
11+
string memory json = getScriptFile(inputScriptFileName);
12+
address evc = vm.parseJsonAddress(json, ".evc");
13+
address eVaultFactory = vm.parseJsonAddress(json, ".eVaultFactory");
14+
address eulerSwapImplementation = vm.parseJsonAddress(json, ".eulerSwapImplementation");
15+
address feeOwner = vm.parseJsonAddress(json, ".feeOwner");
16+
address feeReceipientSetter = vm.parseJsonAddress(json, ".feeReceipientSetter");
17+
18+
eulerSwapFactory = execute(evc, eVaultFactory, eulerSwapImplementation, feeOwner, feeReceipientSetter);
19+
20+
string memory object;
21+
object = vm.serializeAddress("factory", "eulerSwapFactory", eulerSwapFactory);
22+
23+
vm.writeJson(object, string.concat(vm.projectRoot(), "/script/", outputScriptFileName));
24+
}
25+
26+
function deploy(
27+
address evc,
28+
address eVaultFactory,
29+
address eulerSwapImplementation,
30+
address feeOwner,
31+
address feeReceipientSetter
32+
) public broadcast returns (address eulerSwapFactory) {
33+
eulerSwapFactory = execute(evc, eVaultFactory, eulerSwapImplementation, feeOwner, feeReceipientSetter);
34+
}
35+
36+
function execute(
37+
address evc,
38+
address eVaultFactory,
39+
address eulerSwapImplementation,
40+
address feeOwner,
41+
address feeReceipientSetter
42+
) public returns (address eulerSwapFactory) {
43+
bytes memory bytecode = abi.encodePacked(
44+
vm.getCode("out-euler-swap/EulerSwapFactory.sol/EulerSwapFactory.json"),
45+
abi.encode(evc, eVaultFactory, eulerSwapImplementation, feeOwner, feeReceipientSetter)
46+
);
47+
assembly {
48+
eulerSwapFactory := create(0, add(bytecode, 0x20), mload(bytecode))
49+
}
50+
}
51+
}

script/50_CoreAndPeriphery.s.sol

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ import {OFTAdapterUpgradeableDeployer, MintBurnOFTAdapterDeployer} from "./14_OF
3636
import {EdgeFactoryDeployer} from "./15_EdgeFactory.s.sol";
3737
import {EulerEarnImplementation, IntegrationsParams} from "./20_EulerEarnImplementation.s.sol";
3838
import {EulerEarnFactory} from "./21_EulerEarnFactory.s.sol";
39+
import {EulerSwapImplementation} from "./22_EulerSwapImplementation.s.sol";
40+
import {EulerSwapFactory} from "./23_EulerSwapFactory.s.sol";
3941
import {FactoryGovernor} from "./../src/Governor/FactoryGovernor.sol";
4042
import {
4143
IGovernorAccessControlEmergencyFactory,
@@ -82,6 +84,10 @@ contract CoreAndPeriphery is BatchBuilder, SafeMultisendBuilder {
8284
address uniswapV3Router;
8385
uint256 feeFlowInitPrice;
8486
bool deployOFT;
87+
bool deployEulerSwapV1;
88+
address uniswapPoolManager;
89+
address eulerSwapFeeOwner;
90+
address eulerSwapFeeRecipientSetter;
8591
}
8692

8793
struct AdaptiveCurveIRMParams {
@@ -172,7 +178,11 @@ contract CoreAndPeriphery is BatchBuilder, SafeMultisendBuilder {
172178
uniswapV2Router: vm.parseJsonAddress(json, ".uniswapV2Router"),
173179
uniswapV3Router: vm.parseJsonAddress(json, ".uniswapV3Router"),
174180
feeFlowInitPrice: vm.parseJsonUint(json, ".feeFlowInitPrice"),
175-
deployOFT: vm.parseJsonBool(json, ".deployOFT")
181+
deployOFT: vm.parseJsonBool(json, ".deployOFT"),
182+
deployEulerSwapV1: vm.parseJsonBool(json, ".deployEulerSwapV1"),
183+
uniswapPoolManager: vm.parseJsonAddress(json, ".uniswapPoolManager"),
184+
eulerSwapFeeOwner: vm.parseJsonAddress(json, ".eulerSwapFeeOwner"),
185+
eulerSwapFeeRecipientSetter: vm.parseJsonAddress(json, ".eulerSwapFeeRecipientSetter")
176186
});
177187

178188
if (
@@ -253,6 +263,7 @@ contract CoreAndPeriphery is BatchBuilder, SafeMultisendBuilder {
253263
coreAddresses.eulerEarnFactory = deployer.deploy(coreAddresses.eulerEarnImplementation);
254264
} else {
255265
console.log("- EulerEarn factory already deployed. Skipping...");
266+
if (vm.isDir("out-euler-earn")) vm.removeDir("out-euler-earn", true);
256267
}
257268

258269
if (governorAddresses.eVaultFactoryGovernor == address(0)) {
@@ -796,11 +807,11 @@ contract CoreAndPeriphery is BatchBuilder, SafeMultisendBuilder {
796807
adminTimelockControllerParams, wildcardTimelockControllerParams, governorAccessControlEmergencyGuardians
797808
);
798809

799-
governorAddresses.capRiskSteward = CapRiskStewardFactory(peripheryAddresses.capRiskStewardFactory).deploy(
800-
governorAddresses.accessControlEmergencyGovernor,
801-
peripheryAddresses.kinkIRMFactory,
802-
multisigAddresses.DAO
803-
);
810+
//governorAddresses.capRiskSteward = CapRiskStewardFactory(peripheryAddresses.capRiskStewardFactory).deploy(
811+
// governorAddresses.accessControlEmergencyGovernor,
812+
// peripheryAddresses.kinkIRMFactory,
813+
// multisigAddresses.DAO
814+
//);
804815

805816
stopBroadcast();
806817
} else {
@@ -993,6 +1004,34 @@ contract CoreAndPeriphery is BatchBuilder, SafeMultisendBuilder {
9931004
console.log("- Adaptive Curve IRM factory or IRM registry not deployed. Skipping...");
9941005
}
9951006

1007+
if (
1008+
eulerSwapAddresses.eulerSwapV1Implementation == address(0)
1009+
&& eulerSwapAddresses.eulerSwapV1Factory == address(0)
1010+
) {
1011+
if (input.deployEulerSwapV1) {
1012+
{
1013+
console.log("+ Deploying EulerSwap V1 implementation...");
1014+
EulerSwapImplementation deployer = new EulerSwapImplementation();
1015+
eulerSwapAddresses.eulerSwapV1Implementation =
1016+
deployer.deploy(coreAddresses.evc, input.uniswapPoolManager);
1017+
}
1018+
{
1019+
console.log("+ Deploying EulerSwap V1 factory...");
1020+
EulerSwapFactory deployer = new EulerSwapFactory();
1021+
eulerSwapAddresses.eulerSwapV1Factory = deployer.deploy(
1022+
coreAddresses.evc,
1023+
coreAddresses.eVaultFactory,
1024+
eulerSwapAddresses.eulerSwapV1Implementation,
1025+
input.eulerSwapFeeOwner,
1026+
input.eulerSwapFeeRecipientSetter
1027+
);
1028+
}
1029+
} else {
1030+
console.log("- EulerSwap v1 not deployed. Skipping...");
1031+
if (vm.isDir("out-euler-swap")) vm.removeDir("out-euler-swap", true);
1032+
}
1033+
}
1034+
9961035
executeBatch();
9971036

9981037
if (multisendItemExists()) {
@@ -1015,6 +1054,9 @@ contract CoreAndPeriphery is BatchBuilder, SafeMultisendBuilder {
10151054
vm.writeJson(serializeGovernorAddresses(governorAddresses), getScriptFilePath("GovernorAddresses_output.json"));
10161055
vm.writeJson(serializeTokenAddresses(tokenAddresses), getScriptFilePath("TokenAddresses_output.json"));
10171056
vm.writeJson(serializeLensAddresses(lensAddresses), getScriptFilePath("LensAddresses_output.json"));
1057+
vm.writeJson(
1058+
serializeEulerSwapAddresses(eulerSwapAddresses), getScriptFilePath("EulerSwapAddresses_output.json")
1059+
);
10181060
vm.writeJson(serializeBridgeAddresses(bridgeAddresses), getScriptFilePath("BridgeAddresses_output.json"));
10191061
vm.writeJson(serializeBridgeConfigCache(), getScriptFilePath("BridgeConfigCache_output.json"));
10201062

@@ -1042,6 +1084,10 @@ contract CoreAndPeriphery is BatchBuilder, SafeMultisendBuilder {
10421084
vm.writeJson(
10431085
serializeLensAddresses(lensAddresses), getAddressesFilePath("LensAddresses.json", block.chainid)
10441086
);
1087+
vm.writeJson(
1088+
serializeEulerSwapAddresses(eulerSwapAddresses),
1089+
getAddressesFilePath("EulerSwapAddresses.json", block.chainid)
1090+
);
10451091
vm.writeJson(
10461092
serializeBridgeAddresses(bridgeAddresses), getAddressesFilePath("BridgeAddresses.json", block.chainid)
10471093
);

0 commit comments

Comments
 (0)