Skip to content

Commit 234e9ee

Browse files
feat: cluster management and deployment script
1 parent 6909f65 commit 234e9ee

File tree

9 files changed

+346
-41
lines changed

9 files changed

+346
-41
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
DEPLOYMENT_RPC_URL_1=
2+
DEPLOYMENT_RPC_URL_8453=
3+
DEPLOYMENT_RPC_URL_80094=

.gitignore

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
cache/
33
out/
44

5-
# Ignores development broadcast logs
6-
!/broadcast
7-
/broadcast/*/31337/
8-
/broadcast/**/dry-run/
5+
# Ignores the deployment logs
6+
/broadcast
7+
/deployments
98

109
# Docs
1110
docs/

README.md

Lines changed: 99 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,129 @@
1-
## Foundry
1+
# Cluster Deployment and Management
22

3-
**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**
3+
This README explains how to manage a cluster using the provided scripts.
44

5-
Foundry consists of:
5+
## Overview
66

7-
- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
8-
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
9-
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
10-
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.
7+
A cluster is a collection of vaults that work together in the system. The `ManageClusterBase.s.sol` contract defined in the `evk-periphery` repository provides the base functionality for configuring and managing clusters, while specific cluster implementations (like example `Cluster.s.sol`) define the actual configuration for each cluster.
118

12-
## Documentation
9+
## Management Process
1310

14-
https://book.getfoundry.sh/
11+
Refer to the `defineCluster()` and `configureCluster()` functions in the specific cluster script file. If no vaults are deployed yet, they will get deployed when the management script is executed for the first time. If the vaults are already deployed, the management script will only apply the delta between the cluster script file configuration and the current state of the cluster.
1512

16-
## Usage
13+
Edit the specific cluster file (e.g., `Cluster.s.sol`) to set up the desired configuration. Define assets, LTVs, oracle providers, supply caps, borrow caps, IRM parameters, and other settings.
1714

18-
### Build
15+
Note that the cluster specific contracts depend on the `Addresses.s.sol` which defines the asset addresses. You might need to either expand the `AddressesEthereum` contract to include more addresses on mainnet or create a new dedicated contract for new network (i.e. `AddressesBase`) and allow the `Cluster` contract to inherit from it.
1916

20-
```shell
21-
$ forge build
17+
The corresponding `.json` files in the scripts directory that are created when running the script are used as the deployed contracts addresses cache. They are used for further management of the cluster. They must be retained as this is how the existing contract addresses are being loaded into the cluster management script!
18+
19+
## Prerequisites
20+
21+
1. Ensure that you have up to date foundry version installed:
22+
23+
```bash
24+
foundryup
25+
```
26+
27+
If you don't have foundry installed at all, before running `foundryup`, you need to first run:
28+
29+
```bash
30+
curl -L https://foundry.paradigm.xyz | bash
31+
```
32+
33+
2. Clone the repository if you don't have it already:
34+
35+
```bash
36+
git clone https://github.com/euler-xyz/euler-vault-scripts.git && cd euler-vault-scripts
2237
```
2338

24-
### Test
39+
3. Prepare the `.env` file by. Use `.env.example` as an example. Define the RPC URLs for all the chain IDs you need.
2540

26-
```shell
27-
$ forge test
41+
4. Ensure that you have up to date dependencies installed:
42+
43+
```bash
44+
install.sh
2845
```
2946

30-
### Format
47+
5. Compile the contracts:
3148

32-
```shell
33-
$ forge fmt
49+
```bash
50+
forge clean && forge compile
3451
```
3552

36-
### Gas Snapshots
53+
## Run the script
54+
55+
Use the `ExecuteSolidityScript.sh` script to run the management script.
3756

38-
```shell
39-
$ forge snapshot
57+
Use the following command:
58+
59+
```bash
60+
./script/ExecuteSolidityScript.sh script/clusters/<ClusterSpecificScript> [options]
4061
```
4162

42-
### Anvil
63+
Replace `<ClusterSpecificScript>` with the cluster specific file name, i.e. `Cluster.s.sol`.
64+
65+
Options:
66+
- `--dry-run`: Simulates the script without actually executing transactions.
67+
- `--rpc-url URL|CHAIN_ID`: Must be used if `DEPLOYMENT_RPC_URL` not defined in `.env`. If `CHAIN_ID` passed, it will get resolved as per `.env`.
68+
- `--account ACCOUNT` or `--ledger`: Must be used if `DEPLOYER_KEY` not defined in `.env` and the transaction if not meant to be executed via Safe (no actual transaction executed on-chain).
69+
- `--batch-via-safe`: Creates a batch payload file that can be used to create a batch transaction via Safe. This option must be used in case the cluster is managed using Safe (even if not directly; i.e. Safe is a proposer on the timelock controller).
70+
- `--safe-address SAFE_ADDRESS`: Authorized Safe multisig address that will be used to create a batch transaction This option must be used in case the cluster is managed using Safe (even if not directly; i.e. Safe is a proposer on the timelock controller).
71+
- `--timelock-address`: Schedules the transactions in the timelock controller provided instead of trying to execute them immediately. This option must be used in case the timelock controller is installed as part of the governor contracts suite.
72+
- `--risk-steward-address`: Executes the transactions via the risk steward contract provided instead of trying to execute it directly. This option can be used in case the risk steward contract is installed as part of the governor contracts suite and the operation executed allows bypassing timelocks.
73+
74+
Example - initial deployment:
4375

44-
```shell
45-
$ anvil
76+
```
77+
./script/ExecuteSolidityScript.sh ./script/clusters/Cluster.s.sol --account DEPLOYER --rpc-url 1
4678
```
4779

48-
### Deploy
80+
Example - management of the deployed cluster after the governance transferred to the governance contracts suite (GovernorAccessControl + TimelockController + Safe):
4981

50-
```shell
51-
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>
5282
```
83+
./script/ExecuteSolidityScript.sh ./script/clusters/Cluster.s.sol --batch-via-safe --safe-address DAO --timelock-address wildcard --rpc-url 1
84+
```
85+
86+
## Important Notes
87+
88+
Always try to use the `--dry-run` option first to simulate the transactions and check for any potential issues.
89+
90+
# Emergency Vault Pause
91+
92+
In case of a governor contract installed, this section assumes that the cluster is governed by the `GovernorAccessControlEmergency` contract with a Safe multisig having an appropriate emergency role granted to it.
93+
94+
> **IMPORTANT**: Environment variables defined in the `.env` file take precedence over the command line arguments!
5395
54-
### Cast
96+
## Steps
5597

56-
```shell
57-
$ cast <subcommand>
98+
1. Ensure that you performed all the steps from the Prerequisites section.
99+
100+
2. Run the script:
101+
102+
Options:
103+
- `--rpc-url` required if `DEPLOYMENT_RPC_URL` not defined in `.env`
104+
- `--account ACCOUNT` or `--ledger` if `DEPLOYER_KEY` not defined in `.env` and the transaction if not meant to be executed via Safe
105+
- `--batch-via-safe` if operations must be executed via Safe multisig (typically should always be used)
106+
- `--safe-address SAFE_ADDRESS` authorized Safe multisig address
107+
- `--vault-address VAULT_ADDRESS` the vault being a subject of the emegency operation
108+
- `--emergency-ltv-collateral` should be used if you intend to disable the `VAULT_ADDRESS` from being used as collateral by modifying the borrow LTVs
109+
- `--emergency-ltv-borrowing` should be used if you intend to disable all collaterals on the `VAULT_ADDRESS` by modifying the borrow LTVs
110+
- `--emergency-caps` should be used if you intend to set the supply and the borrow caps of the `VAULT_ADDRESS` to zero
111+
- `--emergency-operations` should be used if you intend disable all the operations of the `VAULT_ADDRESS`
112+
113+
```bash
114+
./script/ExecuteSolidityScript.sh PATH_TO_CLUSTER_SPECIFIC_SCRIPT --rpc-url RPC_URL --batch-via-safe --safe-address SAFE_ADDRESS --vault-address VAULT_ADDRESS [--emergency-ltv-collateral] [--emergency-ltv-borrowing] [--emergency-caps] [--emergency-caps]
58115
```
59116

60-
### Help
117+
Example command:
61118

62-
```shell
63-
$ forge --help
64-
$ anvil --help
65-
$ cast --help
119+
```bash
120+
./script/ExecuteSolidityScript.sh script/clusters/Cluster.s.sol --rpc-url https://ethereum-rpc.publicnode.com --batch-via-safe --safe-address 0xB1345E7A4D35FB3E6bF22A32B3741Ae74E5Fba27 --vault-address 0xD8b27CF359b7D15710a5BE299AF6e7Bf904984C2 --emergency-ltv-collateral --emergency-caps
66121
```
122+
123+
3. Create the transaction in the Safe UI (if the transaction if meant to be executed via Safe)
124+
125+
Use the Safe UI Transaction Builder tool to create the transaction. Load the `<payload file>` file created by the script that can be found under the following path: `deployments/[CLUSTER_SPECIFIC_DIRECTORY]/[CHAIN_ID]/output/SafeBatchBuilder_*.json`.
126+
127+
4. Coordinate signing process with the other Safe multisig signers.
128+
129+
5. Execute the transaction in the Safe UI.

foundry.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,13 @@
22
src = "src"
33
out = "out"
44
libs = ["lib"]
5+
solc = "0.8.24"
56

6-
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
7+
evm_version = "cancun"
8+
optimizer = true
9+
optimizer_runs = 20_000
10+
11+
fs_permissions = [
12+
{ access = "read-write", path = "./" },
13+
{ access = "read-write", path = "../euler-interfaces" },
14+
]

install.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/bash
2+
3+
if [ ! -d "../euler-interfaces" ]; then
4+
cd .. && git clone https://github.com/euler-xyz/euler-interfaces.git && cd euler-vault-scripts
5+
else
6+
cd ../euler-interfaces && git pull && cd ../euler-vault-scripts
7+
fi
8+
9+
forge install

remappings.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
openzeppelin-contracts-upgradeable/=lib/evk-periphery/lib/openzeppelin-contracts-upgradeable/contracts
2+
openzeppelin-contracts/=lib/evk-periphery/lib/openzeppelin-contracts/contracts
3+
ethereum-vault-connector/=lib/evk-periphery/lib/ethereum-vault-connector/src
4+
evk-periphery-scripts/=lib/evk-periphery/script/

script/ExecuteSolidityScript.sh

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/bin/bash
2+
3+
set -euo pipefail # Exit on error, undefined vars, and pipeline failures
4+
5+
UTILS_DIR="script/utils"
6+
7+
# Function definitions
8+
setup_utils() {
9+
mkdir -p "$UTILS_DIR"
10+
cp lib/evk-periphery/script/utils/{determineArgs.sh,checkEnvironment.sh,executeForgeScript.sh,getFileNameCounter.sh} "$UTILS_DIR/"
11+
}
12+
13+
cleanup() {
14+
rm -rf "$UTILS_DIR"
15+
}
16+
17+
handle_deployment_files() {
18+
local deployment_dir="$1"
19+
local broadcast_dir="$2"
20+
local jsonName="$3"
21+
22+
mkdir -p "$deployment_dir/broadcast" "$deployment_dir/output"
23+
24+
# Handle broadcast file
25+
counter=$(script/utils/getFileNameCounter.sh "$deployment_dir/broadcast/${jsonName}.json")
26+
cp "$broadcast_dir/run-latest.json" "$deployment_dir/broadcast/${jsonName}_${counter}.json"
27+
28+
# Handle JSON files
29+
for json_file in script/*.json; do
30+
[ -e "$json_file" ] || continue # Skip if no JSON files exist
31+
jsonFileName=$(basename "$json_file")
32+
counter=$(script/utils/getFileNameCounter.sh "$deployment_dir/output/$jsonFileName")
33+
mv "$json_file" "$deployment_dir/output/${jsonFileName%.json}_$counter.json"
34+
done
35+
36+
# Handle CSV files
37+
for csv_file in script/*.csv; do
38+
[ -e "$csv_file" ] || continue # Skip if no CSV files exist
39+
csvFileName=$(basename "$csv_file")
40+
counter=$(script/utils/getFileNameCounter.sh "$deployment_dir/output/$csvFileName")
41+
mv "$csv_file" "$deployment_dir/output/${csvFileName%.csv}_$counter.csv"
42+
done
43+
}
44+
45+
cleanup_json_files() {
46+
for json_file in script/*.json; do
47+
[ -e "$json_file" ] || continue # Skip if no JSON files exist
48+
rm "$json_file"
49+
done
50+
}
51+
52+
# Main script execution
53+
if [ -z "$1" ]; then
54+
echo "Usage: $0 <solidity_script_path>"
55+
exit 1
56+
fi
57+
58+
# Register cleanup function
59+
trap cleanup EXIT
60+
61+
# Process script path
62+
scriptPath="${1#./}"
63+
scriptPath="${scriptPath#script/}"
64+
scriptName=$(basename "$1")
65+
shift
66+
67+
# Setup environment
68+
setup_utils
69+
source .env
70+
eval "$(./script/utils/determineArgs.sh "$@")"
71+
eval 'set -- $SCRIPT_ARGS'
72+
73+
# Check environment
74+
if ! script/utils/checkEnvironment.sh "$@"; then
75+
echo "Environment check failed. Exiting."
76+
exit 1
77+
fi
78+
79+
# Execute forge script
80+
if script/utils/executeForgeScript.sh "$scriptPath" "$@"; then
81+
chainId=$(cast chain-id --rpc-url $DEPLOYMENT_RPC_URL)
82+
deployment_dir="deployments/$scriptName/$chainId"
83+
broadcast_dir="broadcast/${scriptName}/$chainId"
84+
jsonName="${scriptName%.s.*}"
85+
86+
if [[ "$@" == *"--dry-run"* ]]; then
87+
deployment_dir="$deployment_dir/dry-run"
88+
broadcast_dir="$broadcast_dir/dry-run"
89+
fi
90+
91+
handle_deployment_files "$deployment_dir" "$broadcast_dir" "$jsonName"
92+
else
93+
cleanup_json_files
94+
fi

script/clusters/Addresses.s.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
pragma solidity ^0.8.0;
4+
5+
abstract contract AddressesEthereum {
6+
address internal constant USD = address(840);
7+
address internal constant BTC = 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB;
8+
address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
9+
address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
10+
address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7;
11+
address internal constant sUSDS = 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD;
12+
}

0 commit comments

Comments
 (0)