diff --git a/.env.example b/.env.example index ca58182f..cd352e35 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,9 @@ FORK_RPC_URL= DEPLOYMENT_RPC_URL= DEPLOYER_KEY= +SAFE_KEY= +SAFE_ADDRESS= +SAFE_NONCE= VERIFIER_URL= VERIFIER_API_KEY= ADDRESSES_DIR_PATH= \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 7ab27454..bd574b64 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,6 +19,9 @@ [submodule "lib/euler-vault-kit"] path = lib/euler-vault-kit url = https://github.com/euler-xyz/euler-vault-kit +[submodule "lib/surl"] + path = lib/surl + url = https://github.com/memester-xyz/surl [submodule "lib/openzeppelin-contracts-upgradeable"] path = lib/openzeppelin-contracts-upgradeable url = https://github.com/openzeppelin/openzeppelin-contracts-upgradeable diff --git a/README.md b/README.md index 0ce61baa..0c1f18bd 100644 --- a/README.md +++ b/README.md @@ -71,8 +71,6 @@ No warranties are provided and no liability will be accepted for any loss incurr Always include thorough tests when using EVK Periphery to ensure it interacts correctly with your code. -EVK Periphery is currently unaudited and should not be used in production. - ## License (c) 2024 Euler Labs Ltd. diff --git a/foundry.toml b/foundry.toml index adc8c9c5..ed5f56b6 100644 --- a/foundry.toml +++ b/foundry.toml @@ -23,7 +23,8 @@ number_underscore = "preserve" override_spacing = true wrap_comments = true ignore = [ - "script/production/mainnet/DeployCBBTC.s.sol" + "script/production/mainnet/clusters/MEGACluster.s.sol", + "script/production/mainnet/clusters/PrimeCluster.s.sol" ] [profile.default.fuzz] diff --git a/lib/surl b/lib/surl new file mode 160000 index 00000000..034c912a --- /dev/null +++ b/lib/surl @@ -0,0 +1 @@ +Subproject commit 034c912ae9b5e707a5afd21f145b452ad8e800df diff --git a/remappings.txt b/remappings.txt index 24f848ab..09afcbfa 100644 --- a/remappings.txt +++ b/remappings.txt @@ -8,8 +8,9 @@ euler-price-oracle/=lib/euler-price-oracle/src/ euler-price-oracle-test/=lib/euler-price-oracle/test/ fee-flow/=lib/fee-flow/src/ reward-streams/=lib/reward-streams/src/ +surl/=lib/surl/src/ @openzeppelin/contracts/utils/math/=lib/euler-price-oracle/lib/openzeppelin-contracts/contracts/utils/math/ @openzeppelin/contracts/utils/structs/=lib/openzeppelin-contracts/contracts/utils/structs/ @openzeppelin/contracts/utils/introspection/=lib/openzeppelin-contracts/contracts/utils/introspection/ @openzeppelin/contracts/access/=lib/openzeppelin-contracts/contracts/access/ -@openzeppelin/contracts/token/=lib/openzeppelin-contracts/contracts/token/ \ No newline at end of file +@openzeppelin/contracts/token/=lib/openzeppelin-contracts/contracts/token/ diff --git a/script/01_Integrations.s.sol b/script/01_Integrations.s.sol index 704d818c..b8a53020 100644 --- a/script/01_Integrations.s.sol +++ b/script/01_Integrations.s.sol @@ -17,9 +17,12 @@ contract Integrations is ScriptUtils { broadcast returns (address evc, address protocolConfig, address sequenceRegistry, address balanceTracker, address permit2) { + string memory inputScriptFileName = "01_Integrations_input.json"; string memory outputScriptFileName = "01_Integrations_output.json"; + string memory json = getInputConfig(inputScriptFileName); + permit2 = abi.decode(vm.parseJson(json, ".permit2"), (address)); - (evc, protocolConfig, sequenceRegistry, balanceTracker, permit2) = execute(); + (evc, protocolConfig, sequenceRegistry, balanceTracker, permit2) = execute(permit2); string memory object; object = vm.serializeAddress("integrations", "evc", evc); @@ -30,17 +33,18 @@ contract Integrations is ScriptUtils { vm.writeJson(object, string.concat(vm.projectRoot(), "/script/", outputScriptFileName)); } - function deploy() + function deploy(address permit2) public broadcast - returns (address evc, address protocolConfig, address sequenceRegistry, address balanceTracker, address permit2) + returns (address evc, address protocolConfig, address sequenceRegistry, address balanceTracker, address) { - (evc, protocolConfig, sequenceRegistry, balanceTracker, permit2) = execute(); + (evc, protocolConfig, sequenceRegistry, balanceTracker, permit2) = execute(permit2); + return (evc, protocolConfig, sequenceRegistry, balanceTracker, permit2); } - function execute() + function execute(address permit2) public - returns (address evc, address protocolConfig, address sequenceRegistry, address balanceTracker, address permit2) + returns (address evc, address protocolConfig, address sequenceRegistry, address balanceTracker, address) { address deployer = getDeployer(); @@ -48,11 +52,17 @@ contract Integrations is ScriptUtils { protocolConfig = address(new ProtocolConfig(deployer, deployer)); sequenceRegistry = address(new SequenceRegistry()); balanceTracker = address(new TrackingRewardStreams(evc, 14 days)); - permit2 = PERMIT2_ADDRESS; if (permit2.code.length == 0) { - DeployPermit2 deployPermit2 = new DeployPermit2(); - deployPermit2.deployPermit2(); + if (isLocalForkDeployment()) { + DeployPermit2 deployPermit2 = new DeployPermit2(); + deployPermit2.deployPermit2(); + permit2 = PERMIT2_ADDRESS; + } else { + revert("Permit2 not deployed yet under the specified address"); + } } + + return (evc, protocolConfig, sequenceRegistry, balanceTracker, permit2); } } diff --git a/script/07_EVault.s.sol b/script/07_EVault.s.sol index 6616d4d7..71489f88 100644 --- a/script/07_EVault.s.sol +++ b/script/07_EVault.s.sol @@ -5,7 +5,10 @@ pragma solidity ^0.8.0; import {ScriptUtils} from "./utils/ScriptUtils.s.sol"; import {GenericFactory} from "evk/GenericFactory/GenericFactory.sol"; import {EulerRouter} from "euler-price-oracle/EulerRouter.sol"; +import {IEVC} from "ethereum-vault-connector/interfaces/IEthereumVaultConnector.sol"; +import {IEVault} from "evk/EVault/IEVault.sol"; import {EulerRouterFactory} from "../src/EulerRouterFactory/EulerRouterFactory.sol"; +import {EscrowedCollateralPerspective} from "../src/Perspectives/deployed/EscrowedCollateralPerspective.sol"; contract EVaultDeployer is ScriptUtils { function run() public broadcast returns (address oracleRouter, address eVault) { @@ -81,6 +84,72 @@ contract EVaultDeployer is ScriptUtils { } } +contract EVaultSingletonEscrowDeployer is ScriptUtils { + function run() public broadcast returns (address eVault) { + string memory inputScriptFileName = "07_EVaultSingletonEscrow_input.json"; + string memory outputScriptFileName = "07_EVaultSingletonEscrow_output.json"; + string memory json = getInputConfig(inputScriptFileName); + address evc = abi.decode(vm.parseJson(json, ".evc"), (address)); + address escrowedCollateralPerspective = + abi.decode(vm.parseJson(json, ".escrowedCollateralPerspective"), (address)); + address eVaultFactory = abi.decode(vm.parseJson(json, ".eVaultFactory"), (address)); + address asset = abi.decode(vm.parseJson(json, ".asset"), (address)); + + eVault = execute(evc, escrowedCollateralPerspective, eVaultFactory, asset); + + string memory object; + object = vm.serializeAddress("eVault", "eVaultSingletonEscrow", eVault); + vm.writeJson(object, string.concat(vm.projectRoot(), "/script/", outputScriptFileName)); + } + + function deploy(address evc, address escrowedCollateralPerspective, address eVaultFactory, address asset) + public + broadcast + returns (address eVault) + { + eVault = execute(evc, escrowedCollateralPerspective, eVaultFactory, asset); + } + + function execute(address evc, address escrowedCollateralPerspective, address eVaultFactory, address asset) + public + returns (address eVault) + { + eVault = EscrowedCollateralPerspective(escrowedCollateralPerspective).singletonLookup(asset); + + if (eVault == address(0)) { + eVault = address( + GenericFactory(eVaultFactory).createProxy( + address(0), true, abi.encodePacked(asset, address(0), address(0)) + ) + ); + + address deployer = getDeployer(); + IEVC.BatchItem[] memory items = new IEVC.BatchItem[](3); + items[0] = IEVC.BatchItem({ + targetContract: eVault, + onBehalfOfAccount: deployer, + value: 0, + data: abi.encodeCall(IEVault(eVault).setHookConfig, (address(0), 0)) + }); + items[1] = IEVC.BatchItem({ + targetContract: eVault, + onBehalfOfAccount: deployer, + value: 0, + data: abi.encodeCall(IEVault(eVault).setGovernorAdmin, (address(0))) + }); + items[2] = IEVC.BatchItem({ + targetContract: escrowedCollateralPerspective, + onBehalfOfAccount: deployer, + value: 0, + data: abi.encodeCall( + EscrowedCollateralPerspective(escrowedCollateralPerspective).perspectiveVerify, (eVault, true) + ) + }); + IEVC(evc).batch(items); + } + } +} + contract OracleRouterDeployer is ScriptUtils { function run() public broadcast returns (address oracleRouter) { string memory inputScriptFileName = "07_OracleRouter_input.json"; diff --git a/script/interactiveDeployment.sh b/script/interactiveDeployment.sh index 94c6c66a..8ab917ab 100755 --- a/script/interactiveDeployment.sh +++ b/script/interactiveDeployment.sh @@ -148,6 +148,15 @@ while true; do baseName=01_Integrations scriptName=${baseName}.s.sol jsonName=$baseName + + read -p "Enter the PERMIT2 contract address (default: 0x000000000022D473030F116dDEE9F6B43aC78BA3): " permit2 + permit2=${permit2:-0x000000000022D473030F116dDEE9F6B43aC78BA3} + + jq -n \ + --arg permit2 "$permit2" \ + '{ + permit2: $permit2 + }' --indent 4 > script/${jsonName}_input.json ;; 2) echo "Deploying periphery factories..." @@ -593,43 +602,81 @@ while true; do ;; 7) echo "Deploying EVault..." + echo "Select the type of EVault to deploy:" + echo "0. Vanilla EVault" + echo "1. Singleton Escrow EVault" + read -p "Enter your choice (0-1): " vault_choice baseName=07_EVault - scriptName=${baseName}.s.sol:EVaultDeployer - jsonName=$baseName - read -p "Should deploy a new router for the oracle? (y/n) (default: y): " deploy_router_for_oracle - deploy_router_for_oracle=${deploy_router_for_oracle:-y} + case $vault_choice in + 0) + echo "Deploying vanilla EVault..." + + scriptName=${baseName}.s.sol:EVaultDeployer + jsonName=07_EVault - oracle_router_factory=0x0000000000000000000000000000000000000000 - if [[ $deploy_router_for_oracle != "n" ]]; then - read -p "Enter the Oracle Router Factory address: " oracle_router_factory - fi - - read -p "Enter the EVault Factory address: " evault_factory - read -p "Should the vault be upgradable? (y/n) (default: n): " upgradable - upgradable=${upgradable:-n} - read -p "Enter the Asset address: " asset - read -p "Enter the Oracle address: " oracle - read -p "Enter the Unit of Account address: " unit_of_account + read -p "Should deploy a new router for the oracle? (y/n) (default: y): " deploy_router_for_oracle + deploy_router_for_oracle=${deploy_router_for_oracle:-y} - jq -n \ - --argjson deployRouterForOracle "$(jq -n --argjson val \"$deploy_router_for_oracle\" 'if $val != "n" then true else false end')" \ - --arg oracleRouterFactory "$oracle_router_factory" \ - --arg eVaultFactory "$evault_factory" \ - --argjson upgradable "$(jq -n --argjson val \"$upgradable\" 'if $val == "y" then true else false end')" \ - --arg asset "$asset" \ - --arg oracle "$oracle" \ - --arg unitOfAccount "$unit_of_account" \ - '{ - deployRouterForOracle: $deployRouterForOracle, - oracleRouterFactory: $oracleRouterFactory, - eVaultFactory: $eVaultFactory, - upgradable: $upgradable, - asset: $asset, - oracle: $oracle, - unitOfAccount: $unitOfAccount - }' --indent 4 > script/${jsonName}_input.json + oracle_router_factory=0x0000000000000000000000000000000000000000 + if [[ $deploy_router_for_oracle != "n" ]]; then + read -p "Enter the Oracle Router Factory address: " oracle_router_factory + fi + + read -p "Enter the EVault Factory address: " evault_factory + read -p "Should the vault be upgradable? (y/n) (default: n): " upgradable + upgradable=${upgradable:-n} + read -p "Enter the Asset address: " asset + read -p "Enter the Oracle address: " oracle + read -p "Enter the Unit of Account address: " unit_of_account + + jq -n \ + --argjson deployRouterForOracle "$(jq -n --argjson val \"$deploy_router_for_oracle\" 'if $val != "n" then true else false end')" \ + --arg oracleRouterFactory "$oracle_router_factory" \ + --arg eVaultFactory "$evault_factory" \ + --argjson upgradable "$(jq -n --argjson val \"$upgradable\" 'if $val == "y" then true else false end')" \ + --arg asset "$asset" \ + --arg oracle "$oracle" \ + --arg unitOfAccount "$unit_of_account" \ + '{ + deployRouterForOracle: $deployRouterForOracle, + oracleRouterFactory: $oracleRouterFactory, + eVaultFactory: $eVaultFactory, + upgradable: $upgradable, + asset: $asset, + oracle: $oracle, + unitOfAccount: $unitOfAccount + }' --indent 4 > script/${jsonName}_input.json + ;; + 1) + echo "Deploying singleton escrow EVault..." + + scriptName=${baseName}.s.sol:EVaultSingletonEscrowDeployer + jsonName=07_EVaultSingletonEscrow + + read -p "Enter the EVC address: " evc + read -p "Enter the Escrowed Collateral Perspective address: " escrowed_collateral_perspective + read -p "Enter the EVault Factory address: " evault_factory + read -p "Enter the Asset address: " asset + + jq -n \ + --arg evc "$evc" \ + --arg escrowedCollateralPerspective "$escrowed_collateral_perspective" \ + --arg eVaultFactory "$evault_factory" \ + --arg asset "$asset" \ + '{ + evc: $evc, + escrowedCollateralPerspective: $escrowedCollateralPerspective, + eVaultFactory: $eVaultFactory, + asset: $asset + }' --indent 4 > script/${jsonName}_input.json + ;; + *) + echo "Invalid EVault choice. Exiting." + exit 1 + ;; + esac ;; 8) echo "Deploying lenses..." diff --git a/script/production/ExecuteSolidityScript.sh b/script/production/ExecuteSolidityScript.sh index 63e2f47a..762018fc 100755 --- a/script/production/ExecuteSolidityScript.sh +++ b/script/production/ExecuteSolidityScript.sh @@ -15,30 +15,53 @@ verify_contracts=${verify_contracts:-n} if [[ $verify_contracts == "y" ]]; then verify_contracts="--verify" +else + verify_contracts="" fi read -p "Provide the deployment name used to save results (default: default): " deployment_name deployment_name=${deployment_name:-default} -if ! script/utils/checkEnvironment.sh $verify_contracts; then - echo "Environment check failed. Exiting." - exit 1 +if [[ "$@" == *"--batch-via-safe"* ]]; then + batch_via_safe="--batch-via-safe" fi if [[ "$@" == *"--dry-run"* ]]; then dry_run="--dry-run" fi -if script/utils/executeForgeScript.sh "$scriptPath" $verify_contracts $dry_run; then - if [[ $dry_run == "" ]]; then - deployment_dir="script/deployments/$deployment_name" - chainId=$(cast chain-id --rpc-url $DEPLOYMENT_RPC_URL) +if ! script/utils/checkEnvironment.sh $verify_contracts $batch_via_safe; then + echo "Environment check failed. Exiting." + exit 1 +fi +if script/utils/executeForgeScript.sh "$scriptPath" $verify_contracts $batch_via_safe $dry_run; then + chainId=$(cast chain-id --rpc-url $DEPLOYMENT_RPC_URL) + deployment_dir="script/deployments/$deployment_name" + + if [[ $dry_run == "" ]]; then mkdir -p "$deployment_dir/broadcast" "$deployment_dir/output" cp "broadcast/${scriptName}/$chainId/run-latest.json" "$deployment_dir/broadcast/${scriptName}.json" - [ -f "script/CoreAddresses.json" ] && mv "script/CoreAddresses.json" "$deployment_dir/output/CoreAddresses.json" - [ -f "script/PeripheryAddresses.json" ] && mv "script/PeripheryAddresses.json" "$deployment_dir/output/PeripheryAddresses.json" - [ -f "script/LensAddresses.json" ] && mv "script/LensAddresses.json" "$deployment_dir/output/LensAddresses.json" + for json_file in script/*.json; do + jsonFileName=$(basename "$json_file") + counter=$(script/utils/getFileNameCounter.sh "$deployment_dir/output/$jsonFileName") + + mv "$json_file" "$deployment_dir/output/${jsonFileName%.json}_$counter.json" + done + else + mkdir -p "$deployment_dir/dry-run/broadcast" + cp "broadcast/${scriptName}/$chainId/dry-run/run-latest.json" "$deployment_dir/dry-run/broadcast/${scriptName}.json" + + for json_file in script/*.json; do + jsonFileName=$(basename "$json_file") + counter=$(script/utils/getFileNameCounter.sh "$deployment_dir/dry-run/$jsonFileName") + + mv "$json_file" "$deployment_dir/dry-run/${jsonFileName%.json}_$counter.json" + done fi +else + for json_file in script/*.json; do + rm "$json_file" + done fi diff --git a/script/production/arbitrum/DeployCoreAndPeriphery.s.sol b/script/production/arbitrum/DeployCoreAndPeriphery.s.sol index 282e9e23..1cd0b0f3 100644 --- a/script/production/arbitrum/DeployCoreAndPeriphery.s.sol +++ b/script/production/arbitrum/DeployCoreAndPeriphery.s.sol @@ -17,6 +17,7 @@ import {Base} from "evk/EVault/shared/Base.sol"; import {ProtocolConfig} from "evk/ProtocolConfig/ProtocolConfig.sol"; contract DeployCoreAndPeriphery is ScriptUtils { + address internal constant PERMIT2_ADDRESS = 0x000000000022D473030F116dDEE9F6B43aC78BA3; address internal constant WETH = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1; address internal constant UNISWAP_ROUTER_V2 = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; @@ -46,7 +47,7 @@ contract DeployCoreAndPeriphery is ScriptUtils { coreAddresses.sequenceRegistry, coreAddresses.balanceTracker, coreAddresses.permit2 - ) = deployer.deploy(); + ) = deployer.deploy(PERMIT2_ADDRESS); } // deploy periphery factories { diff --git a/script/production/mainnet/ConfigSDAI.s.sol b/script/production/mainnet/ConfigSDAI.s.sol deleted file mode 100644 index 100aacc6..00000000 --- a/script/production/mainnet/ConfigSDAI.s.sol +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pragma solidity ^0.8.0; - -import {BatchBuilder} from "../../utils/ScriptUtils.s.sol"; -import {EulerRouter} from "euler-price-oracle/EulerRouter.sol"; -import {IEVault} from "evk/EVault/IEVault.sol"; - -contract ConfigSDAI is BatchBuilder { - address internal constant USD = address(840); - address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address internal constant wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; - address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; - address internal constant sDAI = 0x83F20F44975D03b1b09e64809B757c47f942BEeA; - address internal constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; - - address internal constant ORACLE_ROUTER = 0x83B3b76873D36A28440cF53371dF404c42497136; - address internal constant DAIUSD = address(0); // TODO - - address internal constant DAO_MULTISIG = 0xcAD001c30E96765aC90307669d578219D4fb1DCe; - address internal constant ORACLE_ROUTER_GOVERNOR = DAO_MULTISIG; - address internal constant RISK_OFF_VAULTS_GOVERNOR = DAO_MULTISIG; - - address internal constant escrowVaultSDAI = address(0); // TODO - mapping(address => address) internal riskOffVaults; - - constructor() { - riskOffVaults[WETH] = 0xD8b27CF359b7D15710a5BE299AF6e7Bf904984C2; - riskOffVaults[wstETH] = 0xbC4B4AC47582c3E38Ce5940B80Da65401F4628f1; - riskOffVaults[USDC] = 0x797DD80692c3b2dAdabCe8e30C07fDE5307D48a9; - riskOffVaults[USDT] = 0x313603FA690301b0CaeEf8069c065862f9162162; - } - - function run() public returns (bytes memory) { - // configure the oracle router - addBatchItem( - ORACLE_ROUTER, ORACLE_ROUTER_GOVERNOR, abi.encodeCall(EulerRouter.govSetConfig, (DAI, USD, DAIUSD)) - ); - addBatchItem( - ORACLE_ROUTER, ORACLE_ROUTER_GOVERNOR, abi.encodeCall(EulerRouter.govSetResolvedVault, (sDAI, true)) - ); - addBatchItem( - ORACLE_ROUTER, - ORACLE_ROUTER_GOVERNOR, - abi.encodeCall(EulerRouter.govSetResolvedVault, (escrowVaultSDAI, true)) - ); - - // configure the LTVs - addBatchItem( - riskOffVaults[WETH], - RISK_OFF_VAULTS_GOVERNOR, - abi.encodeCall(IEVault(riskOffVaults[WETH]).setLTV, (escrowVaultSDAI, 0.74e4, 0.76e4, 0)) - ); - addBatchItem( - riskOffVaults[wstETH], - RISK_OFF_VAULTS_GOVERNOR, - abi.encodeCall(IEVault(riskOffVaults[wstETH]).setLTV, (escrowVaultSDAI, 0.74e4, 0.76e4, 0)) - ); - addBatchItem( - riskOffVaults[USDC], - RISK_OFF_VAULTS_GOVERNOR, - abi.encodeCall(IEVault(riskOffVaults[USDC]).setLTV, (escrowVaultSDAI, 0.85e4, 0.87e4, 0)) - ); - addBatchItem( - riskOffVaults[USDT], - RISK_OFF_VAULTS_GOVERNOR, - abi.encodeCall(IEVault(riskOffVaults[USDT]).setLTV, (escrowVaultSDAI, 0.85e4, 0.87e4, 0)) - ); - - return getBatchCalldata(); - } -} diff --git a/script/production/mainnet/DeployCBBTC.s.sol b/script/production/mainnet/DeployCBBTC.s.sol deleted file mode 100644 index b7885d64..00000000 --- a/script/production/mainnet/DeployCBBTC.s.sol +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pragma solidity ^0.8.0; - -import {BatchBuilder} from "../../utils/ScriptUtils.s.sol"; -import {OracleVerifier} from "../../utils/SanityCheckOracle.s.sol"; -import {PerspectiveVerifier} from "../../utils/PerspectiveCheck.s.sol"; -import {KinkIRM} from "../../04_IRM.s.sol"; -import {EVaultDeployer, OracleRouterDeployer} from "../../07_EVault.s.sol"; - -contract DeployCBBTC is BatchBuilder { - // final governor addresses - address internal constant MULTISIG = 0xcAD001c30E96765aC90307669d578219D4fb1DCe; - address internal constant ORACLE_ROUTER_GOVERNOR = MULTISIG; - address internal constant VAULTS_GOVERNOR = MULTISIG; - - // assets - address internal constant USD = address(840); - address internal constant cbBTC = 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf; - address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address internal constant wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; - address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; - - // oracle adapters - address internal constant cbBTCUSD = 0x04A56636015Ef379e21Bb78aa61069E721D0cf1C; - address internal constant WETHUSD = 0x10674C8C1aE2072d4a75FE83f1E159425fd84E1D; - address internal constant wstETHUSD = 0x02dd5B7ab536629d2235276aBCDf8eb3Af9528D7; - address internal constant USDCUSD = 0x6213f24332D35519039f2afa7e3BffE105a37d3F; - address internal constant USDTUSD = 0x587CABe0521f5065b561A6e68c25f338eD037FF9; - - address internal IRM; - address internal oracleRouter; - address[] internal assets; - address[] internal oracleAdapters; - uint16[] internal borrowableEscrowLTVs; - uint16[] internal borrowableBorrowableLTVs; - - mapping(address => address) internal escrowVaults; - mapping(address => address) internal borrowableVaults; - - constructor() { - assets = [cbBTC, WETH, wstETH, USDC, USDT]; - oracleAdapters = [cbBTCUSD, WETHUSD, wstETHUSD, USDCUSD, USDTUSD]; - - escrowVaults[cbBTC] = 0x7fAeE4175B4Ac5AC117106Ea726b7C373C67a419; - escrowVaults[WETH] = 0xb3b36220fA7d12f7055dab5c9FD18E860e9a6bF8; - escrowVaults[wstETH] = 0xF6E2EfDF175e7a91c8847dade42f2d39A9aE57D4; - escrowVaults[USDC] = 0xB93d4928f39fBcd6C89a7DFbF0A867E6344561bE; - escrowVaults[USDT] = 0x2343b4bCB96EC35D8653Fb154461fc673CB20a7e; - - borrowableVaults[WETH] = 0xD8b27CF359b7D15710a5BE299AF6e7Bf904984C2; - borrowableVaults[wstETH] = 0xbC4B4AC47582c3E38Ce5940B80Da65401F4628f1; - borrowableVaults[USDC] = 0x797DD80692c3b2dAdabCe8e30C07fDE5307D48a9; - borrowableVaults[USDT] = 0x313603FA690301b0CaeEf8069c065862f9162162; - - // cbBTC - borrowableEscrowLTVs = /* escrow cbBTC */ [0.00e4, - /* escrow WETH */ 0.83e4, - /* escrow wstETH */ 0.80e4, - /* escrow USDC */ 0.76e4, - /* escrow USDT */ 0.76e4]; - - // cbBTC - borrowableBorrowableLTVs = /* borrowable cbBTC */ [0.00e4, - /* borrowable WETH */ 0.81e4, - /* borrowable wstETH */ 0.78e4, - /* borrowable USDC */ 0.74e4, - /* borrowable USDT */ 0.74e4]; - } - - function run() public returns (address) { - // deploy the IRM - { - KinkIRM deployer = new KinkIRM(); - - // Base=0% APY Kink(45%)=4% APY Max=304% APY - IRM = deployer.deploy(peripheryAddresses.kinkIRMFactory, 0, 643054912, 18204129717, 1932735283); - } - - // deploy the oracle router - { - OracleRouterDeployer deployer = new OracleRouterDeployer(); - oracleRouter = deployer.deploy(peripheryAddresses.oracleRouterFactory); - } - - // deploy the borrowable cbBTC vault - { - EVaultDeployer deployer = new EVaultDeployer(); - borrowableVaults[cbBTC] = deployer.deploy(coreAddresses.eVaultFactory, true, cbBTC, oracleRouter, USD); - } - - // configure the oracle router - for (uint256 i = 0; i < assets.length; ++i) { - address asset = assets[i]; - govSetResolvedVault(oracleRouter, escrowVaults[asset], true); - govSetResolvedVault(oracleRouter, borrowableVaults[asset], true); - govSetConfig(oracleRouter, asset, USD, oracleAdapters[i]); - } - transferGovernance(oracleRouter, ORACLE_ROUTER_GOVERNOR); - - // configure the borrowable cbBTC vault - setMaxLiquidationDiscount(borrowableVaults[cbBTC], 0.15e4); - setLiquidationCoolOffTime(borrowableVaults[cbBTC], 1); - setInterestRateModel(borrowableVaults[cbBTC], IRM); - - for (uint256 i = 0; i < assets.length; ++i) { - address collateral = escrowVaults[assets[i]]; - uint16 ltv = borrowableEscrowLTVs[i]; - - if (ltv != 0) setLTV(borrowableVaults[cbBTC], collateral, ltv - 0.02e4, ltv, 0); - } - - for (uint256 i = 0; i < assets.length; ++i) { - address collateral = borrowableVaults[assets[i]]; - uint16 ltv = borrowableBorrowableLTVs[i]; - - if (ltv != 0) setLTV(borrowableVaults[cbBTC], collateral, ltv - 0.02e4, ltv, 0); - } - - setHookConfig(borrowableVaults[cbBTC], address(0), 0); - setGovernorAdmin(borrowableVaults[cbBTC], VAULTS_GOVERNOR); - executeBatch(); - - // sanity check the oracle config and perspectives - OracleVerifier.verifyOracleConfig(borrowableVaults[cbBTC]); - PerspectiveVerifier.verifyPerspective( - peripheryAddresses.eulerUngovernedNzxPerspective, - borrowableVaults[cbBTC], - PerspectiveVerifier.E__ORACLE_GOVERNED_ROUTER | PerspectiveVerifier.E__GOVERNOR - ); - - return borrowableVaults[cbBTC]; - } -} diff --git a/script/production/mainnet/DeployCoreAndPeriphery.s.sol b/script/production/mainnet/DeployCoreAndPeriphery.s.sol index 1a5acfa2..e1537898 100644 --- a/script/production/mainnet/DeployCoreAndPeriphery.s.sol +++ b/script/production/mainnet/DeployCoreAndPeriphery.s.sol @@ -17,6 +17,7 @@ import {Base} from "evk/EVault/shared/Base.sol"; import {ProtocolConfig} from "evk/ProtocolConfig/ProtocolConfig.sol"; contract DeployCoreAndPeriphery is ScriptUtils { + address internal constant PERMIT2_ADDRESS = 0x000000000022D473030F116dDEE9F6B43aC78BA3; address internal constant EUL = 0xd9Fcd98c322942075A5C3860693e9f4f03AAE07b; address internal constant UNISWAP_ROUTER_V2 = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; @@ -47,7 +48,7 @@ contract DeployCoreAndPeriphery is ScriptUtils { coreAddresses.sequenceRegistry, coreAddresses.balanceTracker, coreAddresses.permit2 - ) = deployer.deploy(); + ) = deployer.deploy(PERMIT2_ADDRESS); } // deploy periphery factories { diff --git a/script/production/mainnet/DeployInitialVaults.s.sol b/script/production/mainnet/DeployInitialVaults.s.sol deleted file mode 100644 index 8e388d6c..00000000 --- a/script/production/mainnet/DeployInitialVaults.s.sol +++ /dev/null @@ -1,145 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pragma solidity ^0.8.0; - -import {BatchBuilder} from "../../utils/ScriptUtils.s.sol"; -import {KinkIRM} from "../../04_IRM.s.sol"; -import {EVaultDeployer, OracleRouterDeployer} from "../../07_EVault.s.sol"; - -contract DeployInitialVaults is BatchBuilder { - address internal constant USD = address(840); - address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address internal constant wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; - address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; - address[] internal assetsList; - - address internal constant WETHUSD = 0x10674C8C1aE2072d4a75FE83f1E159425fd84E1D; - address internal constant wstETHUSD = 0x02dd5B7ab536629d2235276aBCDf8eb3Af9528D7; - address internal constant USDCUSD = 0x6213f24332D35519039f2afa7e3BffE105a37d3F; - address internal constant USDTUSD = 0x587CABe0521f5065b561A6e68c25f338eD037FF9; - address[] internal oracleAdaptersList; - - address internal constant DAO_MULTISIG = 0xcAD001c30E96765aC90307669d578219D4fb1DCe; - address internal constant ORACLE_ROUTER_GOVERNOR = DAO_MULTISIG; - address internal constant RISK_OFF_VAULTS_GOVERNOR = DAO_MULTISIG; - - address oracleRouter; - mapping(address => address) internal escrowVaults; - mapping(address => address) internal riskOffVaults; - - uint16[][] internal riskOffEscrowLTVs; - uint16[][] internal riskOffRiskOffLTVs; - address[] internal irmList; - - constructor() { - assetsList = [WETH, wstETH, USDC, USDT]; - oracleAdaptersList = [WETHUSD, wstETHUSD, USDCUSD, USDTUSD]; - - riskOffEscrowLTVs = [ - [0, 0.89e4, 0.83e4, 0.83e4], - [0.89e4, 0, 0.8e4, 0.8e4], - [0.76e4, 0.76e4, 0, 0.87e4], - [0.76e4, 0.76e4, 0.87e4, 0] - ]; - - riskOffRiskOffLTVs = [ - [0, 0.87e4, 0.81e4, 0.81e4], - [0.87e4, 0, 0.78e4, 0.78e4], - [0.74e4, 0.74e4, 0, 0.85e4], - [0.74e4, 0.74e4, 0.85e4, 0] - ]; - } - - function run() public returns (address[] memory) { - // deploy the oracle router - { - OracleRouterDeployer deployer = new OracleRouterDeployer(); - oracleRouter = deployer.deploy(peripheryAddresses.oracleRouterFactory); - } - - // deploy the IRMs - { - KinkIRM deployer = new KinkIRM(); - - // Base=0% APY Kink(90%)=2.7% APY Max=82.7% APY - address irmWETH = deployer.deploy(peripheryAddresses.kinkIRMFactory, 0, 218407859, 42500370385, 3865470566); - - // Base=0% APY Kink(45%)=4.75% APY Max=84.75% APY - address irmWstETH = deployer.deploy(peripheryAddresses.kinkIRMFactory, 0, 760869530, 7611888145, 1932735283); - - // Base=0% APY Kink(92%)=6.5% APY Max=66.5% APY - address irmUSDC = deployer.deploy(peripheryAddresses.kinkIRMFactory, 0, 505037995, 41211382066, 3951369912); - - // Base=0% APY Kink(92%)=6.5% APY Max=81.5% APY - address irmUSDT = deployer.deploy(peripheryAddresses.kinkIRMFactory, 0, 505037995, 49166860226, 3951369912); - - irmList = [irmWETH, irmWstETH, irmUSDC, irmUSDT]; - } - - // deploy the vaults - { - EVaultDeployer deployer = new EVaultDeployer(); - for (uint256 i = 0; i < assetsList.length; ++i) { - address asset = assetsList[i]; - escrowVaults[asset] = deployer.deploy(coreAddresses.eVaultFactory, true, asset); - riskOffVaults[asset] = deployer.deploy(coreAddresses.eVaultFactory, true, asset, oracleRouter, USD); - } - } - - // configure the oracle router - for (uint256 i = 0; i < assetsList.length; ++i) { - address asset = assetsList[i]; - govSetConfig(oracleRouter, asset, USD, oracleAdaptersList[i]); - govSetResolvedVault(oracleRouter, escrowVaults[asset], true); - govSetResolvedVault(oracleRouter, riskOffVaults[asset], true); - } - transferGovernance(oracleRouter, ORACLE_ROUTER_GOVERNOR); - - // configure the LTVs - setLTVs(riskOffVaults, escrowVaults, riskOffEscrowLTVs); - setLTVs(riskOffVaults, riskOffVaults, riskOffRiskOffLTVs); - - for (uint256 i = 0; i < assetsList.length; ++i) { - // configure the escrow vaults and verify them by the escrow perspective - address vault = escrowVaults[assetsList[i]]; - setHookConfig(vault, address(0), 0); - setGovernorAdmin(vault, address(0)); - perspectiveVerify(peripheryAddresses.escrowedCollateralPerspective, vault); - - // configure the riskOff vaults and verify them by the whitelist perspective - vault = riskOffVaults[assetsList[i]]; - setMaxLiquidationDiscount(vault, 0.15e4); - setLiquidationCoolOffTime(vault, 1); - setInterestRateModel(vault, irmList[i]); - setHookConfig(vault, address(0), 0); - setGovernorAdmin(vault, RISK_OFF_VAULTS_GOVERNOR); - perspectiveVerify(peripheryAddresses.governedPerspective, vault); - } - - executeBatch(); - - // prepare the results - address[] memory result = new address[](2 * assetsList.length); - for (uint256 i = 0; i < assetsList.length; ++i) { - address asset = assetsList[i]; - result[i] = escrowVaults[asset]; - result[i + assetsList.length] = riskOffVaults[asset]; - } - return result; - } - - function setLTVs( - mapping(address => address) storage vaults, - mapping(address => address) storage collaterals, - uint16[][] storage ltvs - ) internal { - for (uint256 i = 0; i < assetsList.length; ++i) { - for (uint256 j = 0; j < assetsList.length; ++j) { - uint16 ltv = ltvs[i][j]; - - if (ltv != 0) setLTV(vaults[assetsList[j]], collaterals[assetsList[i]], ltv - 0.02e4, ltv, 0); - } - } - } -} diff --git a/script/production/mainnet/DeploySDAI.s.sol b/script/production/mainnet/DeploySDAI.s.sol deleted file mode 100644 index 605bad3d..00000000 --- a/script/production/mainnet/DeploySDAI.s.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -pragma solidity ^0.8.0; - -import {BatchBuilder} from "../../utils/ScriptUtils.s.sol"; -import {EVaultDeployer} from "../../07_EVault.s.sol"; -import {SnapshotRegistry} from "../../../src/SnapshotRegistry/SnapshotRegistry.sol"; - -contract DeploySDAI is BatchBuilder { - address internal constant sDAI = 0x83F20F44975D03b1b09e64809B757c47f942BEeA; - address internal constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F; - - function run() public returns (address escrowVaultSDAI) { - // add sDAI to the external vault registry - addBatchItem(peripheryAddresses.externalVaultRegistry, abi.encodeCall(SnapshotRegistry.add, (sDAI, sDAI, DAI))); - - // deploy the sDAI escrow vault - { - EVaultDeployer deployer = new EVaultDeployer(); - escrowVaultSDAI = deployer.deploy(coreAddresses.eVaultFactory, true, sDAI); - } - - // configure the sDAI escrow vault and verify it by the escrow perspective - setHookConfig(escrowVaultSDAI, address(0), 0); - setGovernorAdmin(escrowVaultSDAI, address(0)); - perspectiveVerify(peripheryAddresses.escrowedCollateralPerspective, escrowVaultSDAI); - executeBatch(); - } -} diff --git a/script/production/mainnet/ManageCluster.s.sol b/script/production/mainnet/ManageCluster.s.sol new file mode 100644 index 00000000..5fb325a3 --- /dev/null +++ b/script/production/mainnet/ManageCluster.s.sol @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.8.0; + +import {BatchBuilder} from "../../utils/ScriptUtils.s.sol"; +import {IRMLens} from "../../../src/Lens/IRMLens.sol"; +import {IEVault} from "evk/EVault/IEVault.sol"; +import {KinkIRM} from "../../04_IRM.s.sol"; +import {EVaultDeployer, OracleRouterDeployer, EulerRouter} from "../../07_EVault.s.sol"; +import {OracleLens} from "../../../src/Lens/OracleLens.sol"; +import {StubOracle} from "../../utils/ScriptUtils.s.sol"; +import "../../../src/Lens/LensTypes.sol"; + +abstract contract Addresses { + address internal constant EULER_DEPLOYER = 0xEe009FAF00CF54C1B4387829aF7A8Dc5f0c8C8C5; + address internal constant EULER_DAO_MULTISIG = 0xcAD001c30E96765aC90307669d578219D4fb1DCe; + + address internal constant USD = address(840); + address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address internal constant wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; + address internal constant cbETH = 0xBe9895146f7AF43049ca1c1AE358B0541Ea49704; + address internal constant WEETH = 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee; + address internal constant ezETH = 0xbf5495Efe5DB9ce00f80364C8B423567e58d2110; + address internal constant RETH = 0xae78736Cd615f374D3085123A210448E74Fc6393; + address internal constant METH = 0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa; + address internal constant RSETH = 0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7; + address internal constant sfrxETH = 0xac3E018457B222d93114458476f3E3416Abbe38F; + address internal constant ETHx = 0xA35b1B31Ce002FBF2058D22F30f95D405200A15b; + address internal constant rswETH = 0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0; + address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; + address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; + address internal constant PYUSD = 0x6c3ea9036406852006290770BEdFcAbA0e23A0e8; + address internal constant USDY = 0x96F6eF951840721AdBF46Ac996b59E0235CB985C; + address internal constant wM = 0x437cc33344a0B27A429f795ff6B469C72698B291; + address internal constant mTBILL = 0xDD629E5241CbC5919847783e6C96B2De4754e438; + address internal constant USDe = 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3; + address internal constant wUSDM = 0x57F5E098CaD7A3D1Eed53991D4d66C45C9AF7812; + address internal constant EURC = 0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c; + address internal constant sUSDe = 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497; + address internal constant USDS = 0xdC035D45d973E3EC169d2276DDab16f1e407384F; + address internal constant sUSDS = 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD; + address internal constant stUSD = 0x0022228a2cc5E7eF0274A7Baa600d44da5aB5776; + address internal constant stEUR = 0x004626A008B1aCdC4c74ab51644093b155e59A23; + address internal constant FDUSD = 0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409; + address internal constant USD0 = 0x73A15FeD60Bf67631dC6cd7Bc5B6e8da8190aCF5; + address internal constant GHO = 0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f; + address internal constant crvUSD = 0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E; + address internal constant FRAX = 0x853d955aCEf822Db058eb8505911ED77F175b99e; + address internal constant tBTC = 0x18084fbA666a33d37592fA2633fD49a74DD93a88; + address internal constant WBTC = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; + address internal constant cbBTC = 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf; + address internal constant LBTC = 0x8236a87084f8B84306f72007F36F2618A5634494; + address internal constant eBTC = 0x657e8C867D8B37dCC18fA4Caead9C45EB088C642; + address internal constant SOLVBTC = 0x7A56E1C57C7475CCf742a1832B028F0456652F97; +} + +abstract contract ManageCluster is Addresses, BatchBuilder { + struct Cluster { + string clusterAddressesPath; + address oracleRoutersGovernor; + address vaultsGovernor; + address[] assets; + address[] vaults; + address[] oracleRouters; + uint32 rampDuration; + uint16[][] ltvs; + address[] auxiliaryVaults; + uint16[][] auxiliaryLTVs; + address unitOfAccount; + address feeReceiver; + uint16 interestFee; + uint16 maxLiquidationDiscount; + uint16 liquidationCoolOffTime; + address hookTarget; + uint32 hookedOps; + uint32 configFlags; + bool forceZeroGovernors; + mapping(address asset => string provider) oracleProviders; + mapping(address asset => uint256 supplyCapNoDecimals) supplyCaps; + mapping(address asset => uint256 borrowCapNoDecimals) borrowCaps; + mapping(address asset => address feeReceiverOverride) feeReceiverOverride; + mapping(address asset => uint16 interestFeeOverride) interestFeeOverride; + mapping(address asset => uint16 maxLiquidationDiscountOverride) maxLiquidationDiscountOverride; + mapping(address asset => uint16 liquidationCoolOffTimeOverride) liquidationCoolOffTimeOverride; + mapping(address asset => address hookTargetOverride) hookTargetOverride; + mapping(address asset => uint32 hookedOpsOverride) hookedOpsOverride; + mapping(address asset => uint32 configFlagsOverride) configFlagsOverride; + mapping(address asset => uint256[4] kinkIRMParams) kinkIRMParams; + mapping( + uint256 baseRate + => mapping(uint256 slope1 => mapping(uint256 slope2 => mapping(uint256 kink => address irm))) + ) kinkIRMMap; + address[] irms; + address stubOracle; + } + + Cluster internal cluster; + mapping(address router => mapping(address vault => mapping(address asset => bool resolved))) internal + pendingResolvedVaults; + mapping(address router => mapping(address base => mapping(address quote => bool set))) internal + pendingConfiguredAdapters; + + modifier initialize() { + vm.pauseGasMetering(); + + configureCluster(); + encodeAmountCaps(cluster.assets, cluster.supplyCaps); + encodeAmountCaps(cluster.assets, cluster.borrowCaps); + + loadCluster(); + checkClusterDataSanity(); + + _; + + dumpCluster(); + verifyCluster(); + } + + function run() public initialize { + // deploy the stub oracle (needed in case pull oracle is meant to be used as it might be stale) + if (cluster.stubOracle == address(0)) { + startBroadcast(); + cluster.stubOracle = address(new StubOracle()); + stopBroadcast(); + } + + // deploy the oracle router + { + OracleRouterDeployer deployer = new OracleRouterDeployer(); + address oracleRouter; + for (uint256 i = 0; i < cluster.assets.length; ++i) { + // deploy only one router if needed and reuse it for other to be deployed vaults + if (cluster.vaults[i] == address(0) && cluster.oracleRouters[i] == address(0)) { + if (oracleRouter == address(0)) { + oracleRouter = deployer.deploy(peripheryAddresses.oracleRouterFactory); + + // if the rest of the configuration will be carried out through safe, + // immediately transfer the governance over this router from the deployer to the safe + if (isBatchViaSafe()) { + addBatchItem( + oracleRouter, + getDeployer(), + abi.encodeCall(EulerRouter(oracleRouter).transferGovernance, (getSafe())) + ); + } + } + + cluster.oracleRouters[i] = oracleRouter; + } + } + } + + // deploy the vaults + { + EVaultDeployer deployer = new EVaultDeployer(); + for (uint256 i = 0; i < cluster.assets.length; ++i) { + // only deploy undefined yet vaults + if (cluster.vaults[i] == address(0)) { + cluster.vaults[i] = deployer.deploy( + coreAddresses.eVaultFactory, + true, + cluster.assets[i], + cluster.oracleRouters[i], + cluster.unitOfAccount + ); + + // if the rest of the configuration will be carried out through safe, + // immediately transfer the governance over this vault from the deployer to the safe + if (isBatchViaSafe()) { + addBatchItem( + cluster.vaults[i], + getDeployer(), + abi.encodeCall(IEVault(cluster.vaults[i]).setGovernorAdmin, (getSafe())) + ); + } + } + } + } + + // execute the EVC batch as the deployer to transfer the governance + executeBatchDirectly(); + + // deploy the IRMs + { + KinkIRM deployer = new KinkIRM(); + for (uint256 i = 0; i < cluster.assets.length; ++i) { + uint256[4] storage p = cluster.kinkIRMParams[cluster.assets[i]]; + address irm = cluster.kinkIRMMap[p[0]][p[1]][p[2]][p[3]]; + + // only deploy those IRMs that haven't been deployed or cached yet + if (irm == address(0) && (p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0)) { + irm = deployer.deploy(peripheryAddresses.kinkIRMFactory, p[0], p[1], p[2], uint32(p[3])); + cluster.kinkIRMMap[p[0]][p[1]][p[2]][p[3]] = irm; + } + + cluster.irms[i] = irm; + } + } + + // configure the vaults and the oracle routers + for (uint256 i = 0; i < cluster.vaults.length; ++i) { + address vault = cluster.vaults[i]; + address asset = IEVault(vault).asset(); + + // configure the oracle router for the vault asset by checking if current configuration differs from + // desired. + // recognize potentially pending transactions by looking up pendingResolvedVaults and + // pendingConfiguredAdapters mappings + { + address oracleRouter = cluster.oracleRouters[i]; + address unitOfAccount = IEVault(vault).unitOfAccount(); + (address base, address adapter,) = + computeRouterConfiguration(asset, unitOfAccount, cluster.oracleProviders[asset]); + + // in case the vault asset is a valid external vault, resolve it in the router + if ( + asset != base && !pendingResolvedVaults[oracleRouter][asset][base] + && EulerRouter(oracleRouter).resolvedVaults(asset) != base + ) { + govSetResolvedVault(oracleRouter, asset, true); + pendingResolvedVaults[oracleRouter][asset][base] = true; + } + + // configure the oracle for the vault asset or the asset of the vault asset + if ( + !pendingConfiguredAdapters[oracleRouter][base][unitOfAccount] + && EulerRouter(oracleRouter).getConfiguredOracle(base, unitOfAccount) != adapter + ) { + govSetConfig(oracleRouter, base, unitOfAccount, adapter); + pendingConfiguredAdapters[oracleRouter][base][unitOfAccount] = true; + } + } + + // configure the vault by checking if current configuration differs from desired. + // recognize potential overrides applicable per asset + { + address feeReceiver = IEVault(vault).feeReceiver(); + if (feeReceiver != cluster.feeReceiver) { + setFeeReceiver(vault, cluster.feeReceiver); + } else if ( + cluster.feeReceiverOverride[asset] != address(0) + && feeReceiver != cluster.feeReceiverOverride[asset] + ) { + setFeeReceiver(vault, cluster.feeReceiverOverride[asset]); + } + } + + { + uint16 interestFee = IEVault(vault).interestFee(); + if (interestFee != cluster.interestFee) { + setInterestFee(vault, cluster.interestFee); + } else if (cluster.interestFeeOverride[asset] != 0 && interestFee != cluster.interestFeeOverride[asset]) + { + setInterestFee(vault, cluster.interestFeeOverride[asset]); + } + } + + { + uint16 maxLiquidationDiscount = IEVault(vault).maxLiquidationDiscount(); + if (maxLiquidationDiscount != cluster.maxLiquidationDiscount) { + setMaxLiquidationDiscount(vault, cluster.maxLiquidationDiscount); + } else if ( + cluster.maxLiquidationDiscountOverride[asset] != 0 + && maxLiquidationDiscount != cluster.maxLiquidationDiscountOverride[asset] + ) { + setMaxLiquidationDiscount(vault, cluster.maxLiquidationDiscountOverride[asset]); + } + } + + { + uint16 liquidationCoolOffTime = IEVault(vault).liquidationCoolOffTime(); + if (liquidationCoolOffTime != cluster.liquidationCoolOffTime) { + setLiquidationCoolOffTime(vault, cluster.liquidationCoolOffTime); + } else if ( + cluster.liquidationCoolOffTimeOverride[asset] != 0 + && liquidationCoolOffTime != cluster.liquidationCoolOffTimeOverride[asset] + ) { + setLiquidationCoolOffTime(vault, cluster.liquidationCoolOffTimeOverride[asset]); + } + } + + { + uint32 configFlags = IEVault(vault).configFlags(); + if (configFlags != cluster.configFlags) { + setConfigFlags(vault, cluster.configFlags); + } else if (cluster.configFlagsOverride[asset] != 0 && configFlags != cluster.configFlagsOverride[asset]) + { + setConfigFlags(vault, cluster.configFlagsOverride[asset]); + } + } + + { + (uint16 supplyCap, uint16 borrowCap) = IEVault(vault).caps(); + if (supplyCap != cluster.supplyCaps[asset] || borrowCap != cluster.borrowCaps[asset]) { + setCaps(vault, cluster.supplyCaps[asset], cluster.borrowCaps[asset]); + } + } + + if (IEVault(vault).interestRateModel() != cluster.irms[i]) { + setInterestRateModel(vault, cluster.irms[i]); + } + + setLTVs(vault, cluster.vaults, getLTVs(cluster.ltvs, i)); + setLTVs(vault, cluster.auxiliaryVaults, getLTVs(cluster.auxiliaryLTVs, i)); + + (address hookTarget, uint32 hookedOps) = IEVault(vault).hookConfig(); + if (hookTarget != cluster.hookTarget || hookedOps != cluster.hookedOps) { + setHookConfig(vault, cluster.hookTarget, cluster.hookedOps); + } else if ( + (cluster.hookTargetOverride[asset] != address(0) && hookTarget != cluster.hookTargetOverride[asset]) + || (cluster.hookedOpsOverride[asset] != 0 && hookedOps != cluster.hookedOpsOverride[asset]) + ) { + setHookConfig( + vault, + cluster.hookTargetOverride[asset] != address(0) && hookTarget != cluster.hookTargetOverride[asset] + ? cluster.hookTargetOverride[asset] + : hookTarget, + cluster.hookedOpsOverride[asset] != 0 && hookedOps != cluster.hookedOpsOverride[asset] + ? cluster.hookedOpsOverride[asset] + : hookedOps + ); + } + + if (IEVault(vault).governorAdmin() != cluster.vaultsGovernor) { + setGovernorAdmin(vault, cluster.vaultsGovernor); + } + } + + // transfer the oracle router governance + for (uint256 i = 0; i < cluster.oracleRouters.length; ++i) { + address oracleRouter = cluster.oracleRouters[i]; + if (EulerRouter(oracleRouter).governor() != cluster.oracleRoutersGovernor) { + transferGovernance(oracleRouter, cluster.oracleRoutersGovernor); + } + } + + executeBatch(); + } + + function configureCluster() internal virtual; + function verifyCluster() internal virtual; + + function computeRouterConfiguration(address base, address quote, string memory provider) + private + view + returns (address, address, bool) + { + address adapter = getValidAdapter(base, quote, provider); + bool useStub = false; + + if (_strEq("ExternalVault|", _substring(provider, 0, bytes("ExternalVault|").length))) { + base = IEVault(base).asset(); + } + + string memory name = EulerRouter(adapter).name(); + if (_strEq(name, "PythOracle") || _strEq(name, "RedstoneCoreOracle")) { + useStub = true; + } + + return (base, adapter, useStub); + } + + // sets LTVs for all passed collaterals of the vault + function setLTVs(address vault, address[] memory collaterals, uint16[] memory ltvs) private { + for (uint256 i = 0; i < collaterals.length; ++i) { + address collateral = collaterals[i]; + address collateralAsset = IEVault(collateral).asset(); + address oracleRouter = IEVault(vault).oracle(); + address unitOfAccount = IEVault(vault).unitOfAccount(); + (address base, address adapter, bool useStub) = + computeRouterConfiguration(collateralAsset, unitOfAccount, cluster.oracleProviders[collateralAsset]); + uint16 liquidationLTV = ltvs[i]; + uint16 borrowLTV = liquidationLTV > 0.02e4 ? liquidationLTV - 0.02e4 : 0; + (uint16 currentBorrowLTV, uint16 targetLiquidationLTV,,,) = IEVault(vault).LTVFull(collateral); + + // configure the oracle router for the collateral before setting the LTV. recognize potentially pending + // transactions by looking up pendingResolvedVaults and pendingConfiguredAdapters mappings + + // resolve the collateral vault in the router to be able to convert shares to assets + if ( + !pendingResolvedVaults[oracleRouter][collateral][collateralAsset] + && EulerRouter(oracleRouter).resolvedVaults(collateral) != collateralAsset + ) { + govSetResolvedVault(oracleRouter, collateral, true); + pendingResolvedVaults[oracleRouter][collateral][collateralAsset] = true; + } + + // in case the collateral vault asset is a valid external vault, resolve it in the router + if ( + collateralAsset != base && !pendingResolvedVaults[oracleRouter][collateralAsset][base] + && EulerRouter(oracleRouter).resolvedVaults(collateralAsset) != base + ) { + govSetResolvedVault(oracleRouter, collateralAsset, true); + pendingResolvedVaults[oracleRouter][collateralAsset][base] = true; + } + + // configure the oracle for the collateral vault asset or the asset of the collateral vault asset + if ( + !pendingConfiguredAdapters[oracleRouter][base][unitOfAccount] + && EulerRouter(oracleRouter).getConfiguredOracle(base, unitOfAccount) != adapter + ) { + govSetConfig(oracleRouter, base, unitOfAccount, adapter); + pendingConfiguredAdapters[oracleRouter][base][unitOfAccount] = true; + } + + // disregard the current liquidation LTV if currently ramping down, only compare target LTVs to figure out + // if setting the LTV is required + if (currentBorrowLTV != borrowLTV || targetLiquidationLTV != liquidationLTV) { + // in case the stub oracle has to be used, append the following batch critical section: + // configure the stub oracle, set LTV, configure the desired oracle + if (useStub) { + govSetConfig_critical(oracleRouter, base, unitOfAccount, cluster.stubOracle); + + setLTV_critical( + vault, + collateral, + borrowLTV, + liquidationLTV, + liquidationLTV >= targetLiquidationLTV ? 0 : cluster.rampDuration + ); + + govSetConfig_critical(oracleRouter, base, unitOfAccount, adapter); + + appendCriticalSectionToBatch(); + } else { + setLTV( + vault, + collateral, + borrowLTV, + liquidationLTV, + liquidationLTV >= targetLiquidationLTV ? 0 : cluster.rampDuration + ); + } + } + } + } + + // extracts LTVs column for a given vault from the LTVs matrix + function getLTVs(uint16[][] memory ltvs, uint256 vaultIndex) private pure returns (uint16[] memory) { + require(ltvs.length == 0 || ltvs[0].length > vaultIndex, "Invalid vault index"); + + uint16[] memory vaultLTVs = new uint16[](ltvs.length); + for (uint256 i = 0; i < ltvs.length; ++i) { + vaultLTVs[i] = ltvs[i][vaultIndex]; + } + return vaultLTVs; + } + + function dumpCluster() private { + string memory result = ""; + result = vm.serializeAddress("cluster", "oracleRouters", cluster.oracleRouters); + result = vm.serializeAddress("cluster", "vaults", cluster.vaults); + result = vm.serializeAddress("cluster", "irms", cluster.irms); + result = vm.serializeAddress("cluster", "auxiliaryVaults", cluster.auxiliaryVaults); + result = vm.serializeAddress("cluster", "stubOracle", cluster.stubOracle); + + vm.writeJson(result, string.concat(vm.projectRoot(), "/script/Cluster.json")); + + if (isBroadcast()) { + if (!_strEq(cluster.clusterAddressesPath, "")) vm.writeJson(result, cluster.clusterAddressesPath); + } + } + + function loadCluster() private { + if (!_strEq(cluster.clusterAddressesPath, "")) { + cluster.clusterAddressesPath = string.concat(vm.projectRoot(), cluster.clusterAddressesPath); + + if (vm.exists(cluster.clusterAddressesPath)) { + string memory json = vm.readFile(cluster.clusterAddressesPath); + cluster.oracleRouters = getAddressesFromJson(json, ".oracleRouters"); + cluster.vaults = getAddressesFromJson(json, ".vaults"); + cluster.irms = getAddressesFromJson(json, ".irms"); + cluster.auxiliaryVaults = getAddressesFromJson(json, ".auxiliaryVaults"); + cluster.stubOracle = getAddressFromJson(json, ".stubOracle"); + } + } + + for (uint256 i = 0; i < cluster.irms.length; ++i) { + InterestRateModelDetailedInfo memory irmInfo = + IRMLens(lensAddresses.irmLens).getInterestRateModelInfo(cluster.irms[i]); + + if (irmInfo.interestRateModelType == InterestRateModelType.KINK) { + KinkIRMInfo memory kinkIRMInfo = abi.decode(irmInfo.interestRateModelParams, (KinkIRMInfo)); + cluster.kinkIRMMap[kinkIRMInfo.baseRate][kinkIRMInfo.slope1][kinkIRMInfo.slope2][kinkIRMInfo.kink] = + cluster.irms[i]; + } + } + + if (cluster.vaults.length == 0) { + cluster.vaults = new address[](cluster.assets.length); + } + + if (cluster.oracleRouters.length == 0) { + cluster.oracleRouters = new address[](cluster.assets.length); + } + + if (cluster.irms.length == 0) { + cluster.irms = new address[](cluster.assets.length); + } + } + + function checkClusterDataSanity() private view { + require(cluster.vaults.length == cluster.assets.length, "Vaults and assets length mismatch"); + require(cluster.oracleRouters.length == cluster.assets.length, "OracleRouters and assets length mismatch"); + require(cluster.irms.length == cluster.assets.length, "IRMs and assets length mismatch"); + require(cluster.ltvs.length == cluster.assets.length, "LTVs and assets length mismatch"); + require( + cluster.auxiliaryLTVs.length == cluster.auxiliaryVaults.length, + "Auxiliary LTVs and auxiliary vaults length mismatch" + ); + + for (uint256 i = 0; i < cluster.vaults.length; ++i) { + require( + cluster.vaults[i] == address(0) || cluster.assets[i] == IEVault(cluster.vaults[i]).asset(), + "Vault asset mismatch" + ); + } + + for (uint256 i = 0; i < cluster.vaults.length; ++i) { + require( + cluster.vaults[i] == address(0) || (cluster.oracleRouters[i] == IEVault(cluster.vaults[i]).oracle()), + "Oracle Router mismatch" + ); + } + + for (uint256 i = 0; i < cluster.auxiliaryVaults.length; ++i) { + require(cluster.auxiliaryVaults[i] != address(0), "Auxiliary vault cannot be zero address"); + } + + for (uint256 i = 0; i < cluster.ltvs.length; ++i) { + require(cluster.ltvs[i].length == cluster.assets.length, "LTVs and assets length mismatch"); + } + + for (uint256 i = 0; i < cluster.auxiliaryLTVs.length; ++i) { + require( + cluster.auxiliaryLTVs[i].length == cluster.assets.length, "Auxiliary LTVs and assets length mismatch" + ); + } + + require(bytes(cluster.clusterAddressesPath).length != 0, "Invalid cluster addresses path"); + require( + cluster.forceZeroGovernors + || (cluster.oracleRoutersGovernor != address(0) && cluster.vaultsGovernor != address(0)), + "Invalid governors" + ); + require(cluster.unitOfAccount != address(0), "Invalid unit of account"); + require(cluster.maxLiquidationDiscount != 0, "Invalid max liquidation discount"); + } +} diff --git a/script/production/mainnet/clusters/MEGACluster.json b/script/production/mainnet/clusters/MEGACluster.json new file mode 100644 index 00000000..0711ec88 --- /dev/null +++ b/script/production/mainnet/clusters/MEGACluster.json @@ -0,0 +1,7 @@ +{ + "auxiliaryVaults": [TODO put the Prime vaults in here], + "irms": [], + "oracleRouters": [], + "stubOracle": "0x0000000000000000000000000000000000000000", + "vaults": [] +} \ No newline at end of file diff --git a/script/production/mainnet/clusters/MEGACluster.s.sol b/script/production/mainnet/clusters/MEGACluster.s.sol new file mode 100644 index 00000000..7ffbe837 --- /dev/null +++ b/script/production/mainnet/clusters/MEGACluster.s.sol @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.8.0; + +import {EulerRouter} from "euler-price-oracle/EulerRouter.sol"; +import {IEVault} from "evk/EVault/IEVault.sol"; +import {ManageCluster} from "../ManageCluster.s.sol"; +import {OracleVerifier} from "../../../utils/SanityCheckOracle.s.sol"; +import {PerspectiveVerifier} from "../../../utils/PerspectiveCheck.s.sol"; +import "evk/EVault/shared/Constants.sol"; + +contract Cluster is ManageCluster { + function configureCluster() internal override { + // define the path to the cluster addresses file here + cluster.clusterAddressesPath = "/script/production/mainnet/clusters/MEGACluster.json"; + + // do not change the order of the assets in the .assets array. if done, it must be reflected in other the other arrays the ltvs matrix. + // if more than one vauls has to be deployed for the same asset, it can be added in the array as many times as needed. + // note however, that mappings may need reworking as they always use asset address as key. + cluster.assets = [WETH, wstETH, cbETH, WEETH, ezETH, RETH, METH, RSETH, sfrxETH, ETHx, rswETH, USDC, USDT, PYUSD, USDY, wM, mTBILL, USDe, wUSDM, EURC, sUSDe, USDS, sUSDS, stUSD, stEUR, FDUSD, USD0, GHO, crvUSD, FRAX, tBTC, WBTC, cbBTC, LBTC, eBTC, SOLVBTC]; + + // define the governors here + cluster.oracleRoutersGovernor = getDeployer(); + cluster.vaultsGovernor = getDeployer(); + + // define unit of account here + cluster.unitOfAccount = USD; + + // define fee receiver here and interest fee here. if needed to be defined per asset, populate the feeReceiverOverride and interestFeeOverride mappings + cluster.feeReceiver = address(0); + cluster.interestFee = 0.1e4; + + // define max liquidation discount here. if needed to be defined per asset, populate the maxLiquidationDiscountOverride mapping + cluster.maxLiquidationDiscount = 0.15e4; + + // define liquidation cool off time here. if needed to be defined per asset, populate the liquidationCoolOffTimeOverride mapping + cluster.liquidationCoolOffTime = 1; + + // define hook target and hooked ops here. if needed to be defined per asset, populate the hookTargetOverride and hookedOpsOverride mappings + cluster.hookTarget = address(0); + cluster.hookedOps = OP_MAX_VALUE - 1; + + // define config flags here. if needed to be defined per asset, populate the configFlagsOverride mapping + cluster.configFlags = 0; + + // define oracle providers here. + // adapter names can be found in the relevant adapter contract (as returned by the `name` function). + // for cross adapters, use the following format: "CrossAdapter=+". + // although Redstone Classic oracles reuse the ChainlinkOracle contract and returns "ChainlinkOracle" name, + // they should be referred to as "RedstoneClassicOracle". + // in case the asset is an ERC4626 vault itself (i.e. sUSDS) and is recognized as a valid external vault as per + // External Vaults Registry, the string should be preceeded by "ExternalVault|" prefix. this is in order to resolve + // the asset (vault) in the oracle router. + // in case the adapter is not present in the Adapter Registry, the adapter address can be passed instead in form of a string. + cluster.oracleProviders[WETH ] = "ChainlinkOracle"; + cluster.oracleProviders[wstETH ] = "CrossAdapter=LidoFundamentalOracle+ChainlinkOracle"; + cluster.oracleProviders[cbETH ] = "CrossAdapter=RateProviderOracle+ChainlinkOracle"; + cluster.oracleProviders[WEETH ] = "CrossAdapter=RateProviderOracle+ChainlinkOracle"; + cluster.oracleProviders[ezETH ] = "CrossAdapter=RateProviderOracle+ChainlinkOracle"; + cluster.oracleProviders[RETH ] = "CrossAdapter=RateProviderOracle+ChainlinkOracle"; + cluster.oracleProviders[METH ] = "CrossAdapter=ChainlinkOracle+ChainlinkOracle"; + cluster.oracleProviders[RSETH ] = "CrossAdapter=RateProviderOracle+ChainlinkOracle"; + cluster.oracleProviders[sfrxETH] = "CrossAdapter=RateProviderOracle+ChainlinkOracle"; + cluster.oracleProviders[ETHx ] = "CrossAdapter=RateProviderOracle+ChainlinkOracle"; + cluster.oracleProviders[rswETH ] = "CrossAdapter=RateProviderOracle+ChainlinkOracle"; + cluster.oracleProviders[USDC ] = "ChainlinkOracle"; + cluster.oracleProviders[USDT ] = "ChainlinkOracle"; + cluster.oracleProviders[PYUSD ] = "ChainlinkOracle"; + cluster.oracleProviders[USDY ] = "PythOracle"; + cluster.oracleProviders[wM ] = "FixedRateOracle"; + cluster.oracleProviders[mTBILL ] = "ChainlinkInfrequentOracle"; + cluster.oracleProviders[USDe ] = "ChainlinkOracle"; + cluster.oracleProviders[wUSDM ] = "ChainlinkOracle"; + cluster.oracleProviders[EURC ] = "PythOracle"; + cluster.oracleProviders[sUSDe ] = "ChainlinkOracle"; + cluster.oracleProviders[USDS ] = "ChronicleOracle"; + cluster.oracleProviders[sUSDS ] = "ExternalVault|ChronicleOracle"; + cluster.oracleProviders[stUSD ] = "ChainlinkOracle"; + cluster.oracleProviders[stEUR ] = "ChainlinkOracle"; + cluster.oracleProviders[FDUSD ] = "PythOracle"; + cluster.oracleProviders[USD0 ] = "ChainlinkOracle"; + cluster.oracleProviders[GHO ] = "ChainlinkOracle"; + cluster.oracleProviders[crvUSD ] = "ChainlinkOracle"; + cluster.oracleProviders[FRAX ] = "ChainlinkOracle"; + cluster.oracleProviders[tBTC ] = "ChainlinkOracle"; + cluster.oracleProviders[WBTC ] = "CrossAdapter=ChainlinkOracle+ChainlinkOracle"; + cluster.oracleProviders[cbBTC ] = "ChainlinkOracle"; + cluster.oracleProviders[LBTC ] = "CrossAdapter=RedstoneClassicOracle+ChainlinkOracle"; + cluster.oracleProviders[eBTC ] = "CrossAdapter=RateProviderOracle+ChainlinkOracle"; + cluster.oracleProviders[SOLVBTC] = "RedstoneClassicOracle"; + + // define supply caps here. 0 means no cap defined hence max amount + cluster.supplyCaps[WETH ] = 378_000; + cluster.supplyCaps[wstETH ] = 160_000; + cluster.supplyCaps[cbETH ] = 8_740; + cluster.supplyCaps[WEETH ] = 36_000; + cluster.supplyCaps[ezETH ] = 9_270; + cluster.supplyCaps[RETH ] = 17_300; + cluster.supplyCaps[METH ] = 18_500; + cluster.supplyCaps[RSETH ] = 9_450; + cluster.supplyCaps[sfrxETH] = 3_890; + cluster.supplyCaps[ETHx ] = 3_740; + cluster.supplyCaps[rswETH ] = 3_880; + cluster.supplyCaps[USDC ] = 500_000_000; + cluster.supplyCaps[USDT ] = 1_000_000_000; + cluster.supplyCaps[PYUSD ] = 25_000_000; + cluster.supplyCaps[USDY ] = 9_520_000; + cluster.supplyCaps[wM ] = 1_000_000; + cluster.supplyCaps[mTBILL ] = 250_000; + cluster.supplyCaps[USDe ] = 50_000_000; + cluster.supplyCaps[wUSDM ] = 2_500_000; + cluster.supplyCaps[EURC ] = 2_200_000; + cluster.supplyCaps[sUSDe ] = 2_270_000; + cluster.supplyCaps[USDS ] = 20_000_000; + cluster.supplyCaps[sUSDS ] = 1_000_000; + cluster.supplyCaps[stUSD ] = 250_000; + cluster.supplyCaps[stEUR ] = 211_000; + cluster.supplyCaps[FDUSD ] = 100_000_000; + cluster.supplyCaps[USD0 ] = 25_000_000; + cluster.supplyCaps[GHO ] = 2_500_000; + cluster.supplyCaps[crvUSD ] = 2_500_000; + cluster.supplyCaps[FRAX ] = 2_500_000; + cluster.supplyCaps[tBTC ] = 157; + cluster.supplyCaps[WBTC ] = 1_570; + cluster.supplyCaps[cbBTC ] = 157; + cluster.supplyCaps[LBTC ] = 157; + cluster.supplyCaps[eBTC ] = 157; + cluster.supplyCaps[SOLVBTC] = 789; + + // define borrow caps here if needed. 0 means no borrow can occur, type(uint256).max means no cap defined hence max amount + + // define IRM classes here and assign them to the assets + { + // Base=0% APY Kink(82%)=2.79% APY Max=122.55% APY + uint256[4] memory irmETH = [uint256(0), uint256(247597527), uint256(31662899097), uint256(3521873182)]; + + // Base=0% APY Kink(82%)=2.79% APY Max=122.55% APY + uint256[4] memory irmBTC = [uint256(0), uint256(247597527), uint256(31662899097), uint256(3521873182)]; + + // Base=0% APY Kink(88%)=5.13% APY Max=101.38% APY + uint256[4] memory irmRWA_T1 = [uint256(0), uint256(419441267), uint256(39964512631), uint256(3779571220)]; + + // Base=0% APY Kink(40%)=4.60% APY Max=145.96% APY + uint256[4] memory irmETH_LST = [uint256(0), uint256(829546015), uint256(10514117840), uint256(1717986918)]; + + // Base=0% APY Kink(25%)=4.60% APY Max=848.77% APY + uint256[4] memory irmETH_LRT = [uint256(0), uint256(1327273625), uint256(21691866441), uint256(1073741824)]; + + // Base=0% APY Kink(25%)=4.60% APY Max=848.77% APY + uint256[4] memory irmBTC_LRT = [uint256(0), uint256(1327273625), uint256(21691866441), uint256(1073741824)]; + + // Base=0% APY Kink(82%)=6.72% APY Max=122.55% APY + uint256[4] memory irmRWA_T2 = [uint256(0), uint256(585195609), uint256(30124952282), uint256(3521873182)]; + + // Base=0% APY Kink(78%)=8.87% APY Max=145.96% APY + uint256[4] memory irmRWA_T3 = [uint256(0), uint256(803876450), uint256(27333024886), uint256(3350074490)]; + + // Base=0% APY Kink(40%)=2.79% APY Max=145.96% APY + uint256[4] memory irmRWA_YLD_T1= [uint256(0), uint256(507574932), uint256(10728765229), uint256(1717986918)]; + + // Base=0% APY Kink(25%)=4.08% APY Max=145.96% APY + uint256[4] memory irmRWA_YLD_T2= [uint256(0), uint256(1180191988), uint256(8460321485), uint256(1073741824)]; + + cluster.kinkIRMParams[WETH ] = irmETH; + cluster.kinkIRMParams[wstETH ] = irmETH_LST; + cluster.kinkIRMParams[cbETH ] = irmETH_LST; + cluster.kinkIRMParams[WEETH ] = irmETH_LRT; + cluster.kinkIRMParams[ezETH ] = irmETH_LRT; + cluster.kinkIRMParams[RETH ] = irmETH_LST; + cluster.kinkIRMParams[METH ] = irmETH_LST; + cluster.kinkIRMParams[RSETH ] = irmETH_LRT; + cluster.kinkIRMParams[sfrxETH] = irmETH_LST; + cluster.kinkIRMParams[ETHx ] = irmETH_LST; + cluster.kinkIRMParams[rswETH ] = irmETH_LRT; + cluster.kinkIRMParams[USDC ] = irmRWA_T1; + cluster.kinkIRMParams[USDT ] = irmRWA_T1; + cluster.kinkIRMParams[PYUSD ] = irmRWA_T2; + cluster.kinkIRMParams[USDY ] = irmRWA_YLD_T1; + cluster.kinkIRMParams[wM ] = irmRWA_T2; + cluster.kinkIRMParams[mTBILL ] = irmRWA_YLD_T1; + cluster.kinkIRMParams[USDe ] = irmRWA_T2; + cluster.kinkIRMParams[wUSDM ] = irmRWA_T2; + cluster.kinkIRMParams[EURC ] = irmRWA_T2; + cluster.kinkIRMParams[sUSDe ] = irmRWA_YLD_T1; + cluster.kinkIRMParams[USDS ] = irmRWA_T2; + cluster.kinkIRMParams[sUSDS ] = irmRWA_YLD_T1; + cluster.kinkIRMParams[stUSD ] = irmRWA_YLD_T2; + cluster.kinkIRMParams[stEUR ] = irmRWA_YLD_T2; + cluster.kinkIRMParams[FDUSD ] = irmRWA_T2; + cluster.kinkIRMParams[USD0 ] = irmRWA_T2; + cluster.kinkIRMParams[GHO ] = irmRWA_T3; + cluster.kinkIRMParams[crvUSD ] = irmRWA_T3; + cluster.kinkIRMParams[FRAX ] = irmRWA_T3; + cluster.kinkIRMParams[tBTC ] = irmBTC; + cluster.kinkIRMParams[WBTC ] = irmBTC; + cluster.kinkIRMParams[cbBTC ] = irmBTC; + cluster.kinkIRMParams[LBTC ] = irmBTC_LRT; + cluster.kinkIRMParams[eBTC ] = irmBTC_LRT; + cluster.kinkIRMParams[SOLVBTC] = irmBTC_LRT; + } + + // define the ramp duration to be used, in case the liquidation LTVs have to be ramped down + cluster.rampDuration = 1 days; + + // define ltv values here. columns are liability vaults, rows are collateral vaults + cluster.ltvs = [ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 + // WETH wstETH cbETH WEETH ezETH RETH METH RSETH sfrxETH ETHx rswETH USDC USDT PYUSD USDY wM mTBILL USDe wUSDM EURC sUSDe USDS sUSDS stUSD stEUR FDUSD USD0 GHO crvUSD FRAX tBTC WBTC cbBTC LBTC eBTC SOLVBTC + /* 0 WETH */ [0.00e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.83e4, 0.83e4, 0.83e4, 0.83e4, 0.83e4, 0.83e4], + /* 1 wstETH */ [0.93e4, 0.00e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.93e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.86e4, 0.79e4, 0.79e4, 0.79e4, 0.79e4, 0.79e4, 0.79e4], + /* 2 cbETH */ [0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4], + /* 3 WEETH */ [0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4], + /* 4 ezETH */ [0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4], + /* 5 RETH */ [0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4], + /* 6 METH */ [0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4], + /* 7 RSETH */ [0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4], + /* 8 sfrxETH */ [0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4], + /* 9 ETHx */ [0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4], + /* 10 rswETH */ [0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4], + /* 11 USDC */ [0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.00e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4], + /* 12 USDT */ [0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.95e4, 0.00e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.95e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4, 0.89e4], + /* 13 PYUSD */ [0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4], + /* 14 USDY */ [0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4], + /* 15 wM */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 16 mTBILL */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 17 USDe */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 18 wUSDM */ [0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4], + /* 19 EURC */ [0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4], + /* 20 sUSDe */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 21 USDS */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 22 sUSDS */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 23 stUSD */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 24 stEUR */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 25 FDUSD */ [0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4], + /* 26 USD0 */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 27 GHO */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 28 crvUSD */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 29 FRAX */ [0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 30 tBTC */ [0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.00e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4], + /* 31 WBTC */ [0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.00e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4], + /* 32 cbBTC */ [0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.00e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4], + /* 33 LBTC */ [0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.00e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.00e4, 0.90e4], + /* 34 eBTC */ [0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.00e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4], + /* 35 SOLVBTC */ [0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.00e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.84e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4] + ]; + + // define auxiliary ltvs here. columns are liability vaults, rows are collateral vaults. + // double check the order of collaterals against the order of auxiliaryVaults in the addresses file + cluster.auxiliaryLTVs = [ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 + // WETH wstETH cbETH WEETH ezETH RETH METH RSETH sfrxETH ETHx rswETH USDC USDT PYUSD USDY wM mTBILL USDe wUSDM EURC sUSDe USDS sUSDS stUSD stEUR FDUSD USD0 GHO crvUSD FRAX tBTC WBTC cbBTC LBTC eBTC SOLVBTC + /* 0 Prime WETH */ [0] + /* 1 Prime wstETH */ + /* 2 Prime cbETH */ + /* 3 Prime WEETH */ + /* 4 Prime USDC */ + /* 5 Prime USDT */ + /* 6 Prime USDS */ + /* 7 Prime tBTC */ + /* 8 Prime WBTC */ + /* 9 Prime cbBTC */ + ]; + } + + function verifyCluster() internal override { + for (uint256 i = 0; i < cluster.vaults.length; ++i) { + perspectiveVerify(peripheryAddresses.governedPerspective, cluster.vaults[i]); + } + executeBatchPrank(EULER_DEPLOYER); + + for (uint256 i = 0; i < cluster.vaults.length; ++i) { + OracleVerifier.verifyOracleConfig(cluster.vaults[i]); + + PerspectiveVerifier.verifyPerspective( + peripheryAddresses.eulerUngovernedNzxPerspective, + cluster.vaults[i], + PerspectiveVerifier.E__ORACLE_GOVERNED_ROUTER | PerspectiveVerifier.E__GOVERNOR, + PerspectiveVerifier.E__LTV_COLLATERAL_RAMPING + ); + } + } +} diff --git a/script/production/mainnet/clusters/PrimeCluster.json b/script/production/mainnet/clusters/PrimeCluster.json new file mode 100644 index 00000000..9cc6129d --- /dev/null +++ b/script/production/mainnet/clusters/PrimeCluster.json @@ -0,0 +1,67 @@ +{ + "auxiliaryVaults": [ + "0xb3b36220fA7d12f7055dab5c9FD18E860e9a6bF8", + "0xF6E2EfDF175e7a91c8847dade42f2d39A9aE57D4", + "0x07e32FF47B1056Ce33B5bDB633Dc925fE52eB5E8", + "0xD440bA5122d68626b5da5399B7157f813735397c", + "0xB93d4928f39fBcd6C89a7DFbF0A867E6344561bE", + "0x2343b4bCB96EC35D8653Fb154461fc673CB20a7e", + "0x98238Ee86f2c571AD06B0913bef21793dA745F57", + "0xe3CA8369346A35b0633da9A4Eb48394478C8BEC2", + "0x44A107DFf1E589f40b22bcBb3757BeCECEAd534e", + "0x9F6d1a62bf268Aa05a1218CFc89C69833D2d2a70", + "0xD185731ECF36Db79787540BC2b4BB40E8F390C9B", + "0x598513C77236Bd5821CCC7bc3E3a585F3FeC9fb1", + "0x7fAeE4175B4Ac5AC117106Ea726b7C373C67a419", + "0xa52EF44448273EABA27C38C47D1Ed9BB28ae2ece" + ], + "irms": [ + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000" + ], + "oracleRouters": [ + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x83B3b76873D36A28440cF53371dF404c42497136", + "0x74C8211626Debe31E296Cacb833f817281b7c299", + "0x83B3b76873D36A28440cF53371dF404c42497136" + ], + "stubOracle": "0x0000000000000000000000000000000000000000", + "vaults": [ + "0xD8b27CF359b7D15710a5BE299AF6e7Bf904984C2", + "0xbC4B4AC47582c3E38Ce5940B80Da65401F4628f1", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x797DD80692c3b2dAdabCe8e30C07fDE5307D48a9", + "0x313603FA690301b0CaeEf8069c065862f9162162", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x9c6e67fA86138Ab49359F595BfE4Fb163D0f16cc", + "0x0000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000", + "0x982403637E2AC3f66B11BefF6438f13d09746478", + "0x0000000000000000000000000000000000000000" + ] +} \ No newline at end of file diff --git a/script/production/mainnet/clusters/PrimeCluster.s.sol b/script/production/mainnet/clusters/PrimeCluster.s.sol new file mode 100644 index 00000000..d1567ff0 --- /dev/null +++ b/script/production/mainnet/clusters/PrimeCluster.s.sol @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.8.0; + +import {ManageCluster} from "../ManageCluster.s.sol"; +import {OracleVerifier} from "../../../utils/SanityCheckOracle.s.sol"; +import {PerspectiveVerifier} from "../../../utils/PerspectiveCheck.s.sol"; + +contract Cluster is ManageCluster { + function configureCluster() internal override { + // define the path to the cluster addresses file here + cluster.clusterAddressesPath = "/script/production/mainnet/clusters/PrimeCluster.json"; + + // do not change the order of the assets in the .assets array. if done, it must be reflected in other the other arrays the ltvs matrix. + // if more than one vauls has to be deployed for the same asset, it can be added in the array as many times as needed. + // note however, that mappings may need reworking as they always use asset address as key. + cluster.assets = [WETH, wstETH, cbETH, WEETH, USDC, USDT, USDS, sUSDS, mTBILL, wM, tBTC, WBTC, cbBTC, LBTC]; + + // define the governors here + cluster.oracleRoutersGovernor = EULER_DAO_MULTISIG; + cluster.vaultsGovernor = EULER_DAO_MULTISIG; + + // define unit of account here + cluster.unitOfAccount = USD; + + // define fee receiver here and interest fee here. if needed to be defined per asset, populate the feeReceiverOverride and interestFeeOverride mappings + cluster.feeReceiver = address(0); + cluster.interestFee = 0.1e4; + + // define max liquidation discount here. if needed to be defined per asset, populate the maxLiquidationDiscountOverride mapping + cluster.maxLiquidationDiscount = 0.15e4; + + // define liquidation cool off time here. if needed to be defined per asset, populate the liquidationCoolOffTimeOverride mapping + cluster.liquidationCoolOffTime = 1; + + // define hook target and hooked ops here. if needed to be defined per asset, populate the hookTargetOverride and hookedOpsOverride mappings + cluster.hookTarget = address(0); + cluster.hookedOps = 0; + + // define config flags here. if needed to be defined per asset, populate the configFlagsOverride mapping + cluster.configFlags = 0; + + // define oracle providers here. + // adapter names can be found in the relevant adapter contract (as returned by the `name` function). + // for cross adapters, use the following format: "CrossAdapter=+". + // although Redstone Classic oracles reuse the ChainlinkOracle contract and returns "ChainlinkOracle" name, + // they should be referred to as "RedstoneClassicOracle". + // in case the asset is an ERC4626 vault itself (i.e. sUSDS) and is recognized as a valid external vault as per + // External Vaults Registry, the string should be preceeded by "ExternalVault|" prefix. this is in order to resolve + // the asset (vault) in the oracle router. + // in case the adapter is not present in the Adapter Registry, the adapter address can be passed instead in form of a string. + cluster.oracleProviders[WETH ] = "ChainlinkOracle"; + cluster.oracleProviders[wstETH ] = "CrossAdapter=LidoOracle+ChainlinkOracle"; + cluster.oracleProviders[cbETH ] = "CrossAdapter=ChainlinkOracle+ChainlinkOracle"; + cluster.oracleProviders[WEETH ] = "CrossAdapter=ChainlinkOracle+ChainlinkOracle"; + cluster.oracleProviders[USDC ] = "ChainlinkOracle"; + cluster.oracleProviders[USDT ] = "ChainlinkOracle"; + cluster.oracleProviders[USDS ] = "ChronicleOracle"; + cluster.oracleProviders[sUSDS ] = "ExternalVault|ChronicleOracle"; + cluster.oracleProviders[mTBILL ] = "0x256f8fA018e8e6F5B54b1fF708efd5ec73E20AC6"; + cluster.oracleProviders[wM ] = "FixedRateOracle"; + cluster.oracleProviders[tBTC ] = "ChainlinkOracle"; + cluster.oracleProviders[WBTC ] = "CrossAdapter=ChainlinkOracle+ChainlinkOracle"; + cluster.oracleProviders[cbBTC ] = "CrossAdapter=ChronicleOracle+ChainlinkOracle"; + cluster.oracleProviders[LBTC ] = "CrossAdapter=RedstoneClassicOracle+ChainlinkOracle"; + + // define supply caps here. 0 means no supply can occur, type(uint256).max means no cap defined hence max amount + cluster.supplyCaps[WETH ] = 378_000; + cluster.supplyCaps[wstETH ] = 160_000; + cluster.supplyCaps[cbETH ] = 8_740; + cluster.supplyCaps[WEETH ] = 36_000; + cluster.supplyCaps[USDC ] = 1_000_000_000; + cluster.supplyCaps[USDT ] = 1_000_000_000; + cluster.supplyCaps[USDS ] = 50_000_000; + cluster.supplyCaps[sUSDS ] = 45_000_000; + cluster.supplyCaps[mTBILL ] = 2_500_000; + cluster.supplyCaps[wM ] = 2_500_000; + cluster.supplyCaps[tBTC ] = 157; + cluster.supplyCaps[WBTC ] = 1_570; + cluster.supplyCaps[cbBTC ] = 157; + cluster.supplyCaps[LBTC ] = 157; + + // define borrow caps here. 0 means no borrow can occur, type(uint256).max means no cap defined hence max amount + cluster.borrowCaps[WETH ] = 321_000; + cluster.borrowCaps[wstETH ] = 64_000; + cluster.borrowCaps[cbETH ] = 3_490; + cluster.borrowCaps[WEETH ] = 9_010; + cluster.borrowCaps[USDC ] = 880_000_000; + cluster.borrowCaps[USDT ] = 880_000_000; + cluster.borrowCaps[USDS ] = 41_000_000; + cluster.borrowCaps[sUSDS ] = 18_000_000; + cluster.borrowCaps[mTBILL ] = 1_000_000; + cluster.borrowCaps[wM ] = 2_050_000; + cluster.borrowCaps[tBTC ] = 133; + cluster.borrowCaps[WBTC ] = 1_330; + cluster.borrowCaps[cbBTC ] = 133; + cluster.borrowCaps[LBTC ] = 39; + + // define IRM classes here and assign them to the assets + { + // Base=0% APY Kink(85%)=2.79% APY Max=122.55% APY + uint256[4] memory irmETH = [uint256(0), uint256(238858791), uint256(37995478916), uint256(3650722201)]; + + // Base=0% APY, Kink(85%)=2.79% APY Max=122.55% APY + uint256[4] memory irmBTC = [uint256(0), uint256(238858791), uint256(37995478916), uint256(3650722201)]; + + // Base=0% APY, Kink(88%)=5.13% APY Max=101.38% APY + uint256[4] memory irmRWA_1 = [uint256(0), uint256(419441267), uint256(39964512631), uint256(3779571220)]; + + // Base=0% APY, Kink(40%)=4.60% APY Max=145.96% APY + uint256[4] memory irmETH_LST = [uint256(0), uint256(829546015), uint256(10514117840), uint256(1717986918)]; + + // Base=0% APY, Kink(25%)=4.60% APY Max=848.77% APY + uint256[4] memory irmETH_LRT = [uint256(0), uint256(1327273625), uint256(21691866441), uint256(1073741824)]; + + // Base=0% APY, Kink(25%)=4.60% APY Max=848.77% APY + uint256[4] memory irmBTC_LRT = [uint256(0), uint256(1327273625), uint256(21691866441), uint256(1073741824)]; + + // Base=0% APY, Kink(82%)=6.72% APY Max=122.55% APY + uint256[4] memory irmRWA_2 = [uint256(0), uint256(585195609), uint256(30124952282), uint256(3521873182)]; + + // Base=0% APY, Kink(40%)=2.79% APY Max=145.96% APY + uint256[4] memory irmRWA_YLD_1 = [uint256(0), uint256(507574932), uint256(10728765229), uint256(1717986918)]; + + cluster.kinkIRMParams[WETH ] = irmETH; + cluster.kinkIRMParams[wstETH ] = irmETH_LST; + cluster.kinkIRMParams[cbETH ] = irmETH_LST; + cluster.kinkIRMParams[WEETH ] = irmETH_LRT; + cluster.kinkIRMParams[USDC ] = irmRWA_1; + cluster.kinkIRMParams[USDT ] = irmRWA_1; + cluster.kinkIRMParams[USDS ] = irmRWA_2; + cluster.kinkIRMParams[sUSDS ] = irmRWA_YLD_1; + cluster.kinkIRMParams[mTBILL ] = irmRWA_YLD_1; + cluster.kinkIRMParams[wM ] = irmRWA_2; + cluster.kinkIRMParams[tBTC ] = irmBTC; + cluster.kinkIRMParams[WBTC ] = irmBTC; + cluster.kinkIRMParams[cbBTC ] = irmBTC; + cluster.kinkIRMParams[LBTC ] = irmBTC_LRT; + } + + // define the ramp duration to be used, in case the liquidation LTVs have to be ramped down + cluster.rampDuration = 1 days; + + // define ltv values here. columns are liability vaults, rows are collateral vaults + cluster.ltvs = [ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + // WETH wstETH cbETH WEETH USDC USDT USDS sUSDS mTBILL wM tBTC WBTC cbBTC LBTC + /* 0 WETH */ [0.00e4, 0.90e4, 0.93e4, 0.88e4, 0.85e4, 0.85e4, 0.89e4, 0.77e4, 0.89e4, 0.89e4, 0.80e4, 0.82e4, 0.83e4, 0.83e4], + /* 1 wstETH */ [0.93e4, 0.00e4, 0.93e4, 0.88e4, 0.85e4, 0.85e4, 0.86e4, 0.77e4, 0.86e4, 0.86e4, 0.79e4, 0.79e4, 0.79e4, 0.79e4], + /* 2 cbETH */ [0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.83e4, 0.83e4, 0.83e4, 0.83e4, 0.83e4, 0.83e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4], + /* 3 WEETH */ [0.92e4, 0.90e4, 0.92e4, 0.00e4, 0.83e4, 0.83e4, 0.83e4, 0.77e4, 0.83e4, 0.83e4, 0.75e4, 0.75e4, 0.75e4, 0.75e4], + /* 4 USDC */ [0.85e4, 0.82e4, 0.89e4, 0.80e4, 0.00e4, 0.95e4, 0.95e4, 0.85e4, 0.95e4, 0.95e4, 0.77e4, 0.82e4, 0.89e4, 0.89e4], + /* 5 USDT */ [0.85e4, 0.82e4, 0.89e4, 0.80e4, 0.95e4, 0.00e4, 0.95e4, 0.85e4, 0.95e4, 0.95e4, 0.77e4, 0.82e4, 0.89e4, 0.89e4], + /* 6 USDS */ [0.83e4, 0.83e4, 0.83e4, 0.83e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4, 0.83e4, 0.83e4, 0.83e4, 0.83e4], + /* 7 sUSDS */ [0.83e4, 0.82e4, 0.83e4, 0.80e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.92e4, 0.77e4, 0.82e4, 0.83e4, 0.83e4], + /* 8 mTBILL */ [0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4], + /* 9 wM */ [0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4, 0.71e4, 0.71e4, 0.71e4, 0.71e4], + /* 10 tBTC */ [0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.80e4, 0.80e4, 0.80e4, 0.77e4, 0.80e4, 0.80e4, 0.00e4, 0.90e4, 0.90e4, 0.90e4], + /* 11 WBTC */ [0.75e4, 0.75e4, 0.75e4, 0.75e4, 0.83e4, 0.83e4, 0.83e4, 0.77e4, 0.83e4, 0.83e4, 0.85e4, 0.00e4, 0.92e4, 0.92e4], + /* 12 cbBTC */ [0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.80e4, 0.80e4, 0.80e4, 0.80e4, 0.80e4, 0.80e4, 0.90e4, 0.90e4, 0.00e4, 0.90e4], + /* 13 LBTC */ [0.71e4, 0.71e4, 0.71e4, 0.71e4, 0.80e4, 0.80e4, 0.80e4, 0.80e4, 0.80e4, 0.80e4, 0.90e4, 0.90e4, 0.90e4, 0.00e4] + ]; + + // define auxiliary ltvs here. columns are liability vaults, rows are collateral vaults. + // double check the order of collaterals against the order of auxiliaryVaults in the addresses file + cluster.auxiliaryLTVs = [ + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + // WETH wstETH cbETH WEETH USDC USDT USDS sUSDS mTBILL wM tBTC WBTC cbBTC LBTC + /* 0 Escrow WETH */ [0.00e4, 0.92e4, 0.95e4, 0.90e4, 0.87e4, 0.87e4, 0.91e4, 0.79e4, 0.91e4, 0.91e4, 0.82e4, 0.84e4, 0.85e4, 0.85e4], + /* 1 Escrow wstETH */ [0.95e4, 0.00e4, 0.95e4, 0.90e4, 0.87e4, 0.87e4, 0.88e4, 0.79e4, 0.88e4, 0.88e4, 0.81e4, 0.81e4, 0.81e4, 0.81e4], + /* 2 Escrow cbETH */ [0.94e4, 0.94e4, 0.00e4, 0.94e4, 0.85e4, 0.85e4, 0.85e4, 0.85e4, 0.85e4, 0.85e4, 0.77e4, 0.77e4, 0.77e4, 0.77e4], + /* 3 Escrow WEETH */ [0.94e4, 0.92e4, 0.94e4, 0.00e4, 0.85e4, 0.85e4, 0.85e4, 0.79e4, 0.85e4, 0.85e4, 0.77e4, 0.77e4, 0.77e4, 0.77e4], + /* 4 Escrow USDC */ [0.87e4, 0.84e4, 0.91e4, 0.82e4, 0.00e4, 0.97e4, 0.97e4, 0.87e4, 0.97e4, 0.97e4, 0.79e4, 0.84e4, 0.91e4, 0.91e4], + /* 5 Escrow USDT */ [0.87e4, 0.84e4, 0.91e4, 0.82e4, 0.97e4, 0.00e4, 0.97e4, 0.87e4, 0.97e4, 0.97e4, 0.79e4, 0.84e4, 0.91e4, 0.91e4], + /* 6 Escrow USDS */ [0.85e4, 0.85e4, 0.85e4, 0.85e4, 0.94e4, 0.94e4, 0.00e4, 0.94e4, 0.94e4, 0.94e4, 0.85e4, 0.85e4, 0.85e4, 0.85e4], + /* 7 Escrow sUSDS */ [0.85e4, 0.84e4, 0.85e4, 0.82e4, 0.94e4, 0.94e4, 0.94e4, 0.00e4, 0.94e4, 0.94e4, 0.79e4, 0.84e4, 0.85e4, 0.85e4], + /* 8 Escrow mTBILL */ [0.73e4, 0.73e4, 0.73e4, 0.73e4, 0.94e4, 0.94e4, 0.94e4, 0.94e4, 0.00e4, 0.94e4, 0.73e4, 0.73e4, 0.73e4, 0.73e4], + /* 9 Escrow wM */ [0.73e4, 0.73e4, 0.73e4, 0.73e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4, 0.73e4, 0.73e4, 0.73e4, 0.73e4], + /* 10 Escrow tBTC */ [0.73e4, 0.73e4, 0.73e4, 0.73e4, 0.82e4, 0.82e4, 0.82e4, 0.79e4, 0.82e4, 0.82e4, 0.00e4, 0.92e4, 0.92e4, 0.92e4], + /* 11 Escrow WBTC */ [0.77e4, 0.77e4, 0.77e4, 0.77e4, 0.85e4, 0.85e4, 0.85e4, 0.79e4, 0.85e4, 0.85e4, 0.87e4, 0.00e4, 0.94e4, 0.94e4], + /* 12 Escrow cbBTC */ [0.73e4, 0.73e4, 0.73e4, 0.73e4, 0.82e4, 0.82e4, 0.82e4, 0.82e4, 0.82e4, 0.82e4, 0.92e4, 0.92e4, 0.00e4, 0.92e4], + /* 13 Escrow LBTC */ [0.73e4, 0.73e4, 0.73e4, 0.73e4, 0.82e4, 0.82e4, 0.82e4, 0.82e4, 0.82e4, 0.82e4, 0.92e4, 0.92e4, 0.92e4, 0.00e4] + ]; + } + + function verifyCluster() internal override { + for (uint256 i = 0; i < cluster.vaults.length; ++i) { + perspectiveVerify(peripheryAddresses.governedPerspective, cluster.vaults[i]); + } + executeBatchPrank(EULER_DEPLOYER); + + for (uint256 i = 0; i < cluster.vaults.length; ++i) { + OracleVerifier.verifyOracleConfig(cluster.vaults[i]); + + PerspectiveVerifier.verifyPerspective( + peripheryAddresses.eulerUngovernedNzxPerspective, + cluster.vaults[i], + PerspectiveVerifier.E__ORACLE_GOVERNED_ROUTER | PerspectiveVerifier.E__GOVERNOR, + PerspectiveVerifier.E__LTV_COLLATERAL_RAMPING | PerspectiveVerifier.E__ORACLE_INVALID_ADAPTER + ); + } + } +} diff --git a/script/production/mainnet/clusters/README.md b/script/production/mainnet/clusters/README.md new file mode 100644 index 00000000..bda78a90 --- /dev/null +++ b/script/production/mainnet/clusters/README.md @@ -0,0 +1,69 @@ +# Cluster Deployment and Management + +This README explains how to manage a cluster using the provided scripts. + +## Overview + +A cluster is a collection of vaults that work together in the system. The `ManageCluster.s.sol` contract provides the base functionality for configuring and managing clusters, while specific cluster implementations (like `PrimeCluster.s.sol` or `MEGACluster.s.sol`) define the actual configuration for each cluster. + +## Management Process + +Refer to the `configureCluster()` function 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. + +Edit the specific cluster file (e.g., `PrimeCluster.s.sol` or `MEGACluster.s.sol`) to set up the desired configuration. Define assets, LTVs, oracle providers, supply caps, borrow caps, IRM parameters, and other settings. + +The corresponding `.json` files created in the scripts directory are used as the deployed contracts addresses cache. They are used for further management of the cluster. This is how the existing contract addresses are being loaded into the management script. + +## Run the script: + +Use the `ExecuteSolidityScript.sh` script to run the management script. + +Use the following command: + +```bash +./script/production/ExecuteSolidityScript.sh script/production/mainnet/clusters/ [options] +``` + +Replace `` with the cluster specific file name, i.e. `PrimeCluster.s.sol`. + +Options: + +`--dry-run`: Simulates the deployment without actually executing transactions. + +`--batch-via-safe`: Creates the configuration batch transactions in the Safe UI. Contracts deployment and auxiliary transactions are still being performed by the deployer account (the account associated with the `DEPLOYER_KEY`) and only the configuration transactions are created in the Safe UI. For this option to be used, ensure that `SAFE_KEY` and `SAFE_ADDRESS` are defined in the `.env` file. The address associated with the `SAFE_KEY` must either be a signer or a delegate of the safe in order to be able to create the transactions in the Safe UI. + +`--verify`: Verifies the deployed contracts (if any) in the blockchain explorer. + +## Important Notes + +Always try to use the `--dry-run` option first to simulate the transactions and check for any potential issues. + +# Safe Delegates management + +To add a new Safe delegate, run: + +```bash +source .env && forge script script/utils/SafeUtils.s.sol:SafeDelegation --sig "create(address,address,string)" $SAFE_ADDRESS