From 682d3b2e5413f44d79c19c1c7fb6d6ccd00e0c51 Mon Sep 17 00:00:00 2001 From: Jun Kim <64379343+junkim012@users.noreply.github.com> Date: Sun, 8 Sep 2024 23:36:09 -0400 Subject: [PATCH 1/4] chore: ssETH deployment fix: deposit enough in order to withdraw from the vault --- .env.example | 9 +++-- Makefile | 14 +++---- deployment-config/chains/1.json | 4 +- deployment-config/ssETH-L1.json | 65 +++++++++++++++++++++++++++++++++ deployment-config/ssETH-L2.json | 57 +++++++++++++++++++++++++++++ lzConfigCheck.cjs | 8 ++++ test/LiveDeploy.t.sol | 16 ++++---- 7 files changed, 153 insertions(+), 20 deletions(-) create mode 100644 deployment-config/ssETH-L1.json create mode 100644 deployment-config/ssETH-L2.json diff --git a/.env.example b/.env.example index de08de7..664bd1e 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,3 @@ -ETHERFI_LIQUID_DEPLOYER= - MAINNET_RPC_URL= TENDERLY_RPC_URL= SEPOLIA_RPC_URL= @@ -7,4 +5,9 @@ SEI_DEVNET_RPC_URL= OP_SEPOLIA_RPC_URL= ETH_FROM= -PRIVATE_KEY= \ No newline at end of file +PRIVATE_KEY= + +L1_RPC_URL= +L2_RPC_URL= +CREATEX= +LIVE_DEPLOY_READ_FILE_NAME= \ No newline at end of file diff --git a/Makefile b/Makefile index 935477e..3ded71e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,8 @@ include .env +check-configs: + bun lzConfigCheck.cjs + checkL1: @echo "Setting environment variable LIVE_DEPLOY_READ_FILE_NAME to $(file)" @export LIVE_DEPLOY_READ_FILE_NAME=$(file) && forge test --mp test/LiveDeploy.t.sol --fork-url=${L1_RPC_URL} @@ -14,7 +17,7 @@ deployL1: deployL2: @echo "Setting environment variable LIVE_DEPLOY_READ_FILE_NAME to $(file)" - @export LIVE_DEPLOY_READ_FILE_NAME=$(file) && forge script script/deploy/deployAll.s.sol --sig "run(string)" $(file) --fork-url=${L1_RPC_URL} + @export LIVE_DEPLOY_READ_FILE_NAME=$(file) && forge script script/deploy/deployAll.s.sol --sig "run(string)" $(file) --fork-url=${L2_RPC_URL} live-deployL1: @echo "Setting environment variable LIVE_DEPLOY_READ_FILE_NAME to $(file)" @@ -22,7 +25,7 @@ live-deployL1: live-deployL2: @echo "Setting environment variable LIVE_DEPLOY_READ_FILE_NAME to $(file)" - @export LIVE_DEPLOY_READ_FILE_NAME=$(file) && forge script script/deploy/deployAll.s.sol --sig "run(string)" $(file) --fork-url=${L1_RPC_URL} --private-key=$(PRIVATE_KEY) --broadcast --slow --verify + @export LIVE_DEPLOY_READ_FILE_NAME=$(file) && forge script script/deploy/deployAll.s.sol --sig "run(string)" $(file) --fork-url=${L2_RPC_URL} --private-key=$(PRIVATE_KEY) --broadcast --slow prettier: prettier --write '**/*.{md,yml,yaml,ts,js}' @@ -37,10 +40,7 @@ prepare: husky deploy-createx-l1: - forge script script/DeployCustomCreatex.s.sol --rpc-url $L1_RPC_URL --private-key $PRIVATE_KEY --slow --no-metadata + forge script script/DeployCustomCreatex.s.sol --rpc-url ${L1_RPC_URL} --private-key ${PRIVATE_KEY} --slow --no-metadata deploy-createx-l2: - forge script script/DeployCustomCreatex.s.sol --rpc-url $L2_RPC_URL --private-key $PRIVATE_KEY --slow --no-metadata - -check-configs: - bun lzConfigCheck.cjs \ No newline at end of file + forge script script/DeployCustomCreatex.s.sol --rpc-url ${L2_RPC_URL} --private-key ${PRIVATE_KEY} --slow --no-metadata \ No newline at end of file diff --git a/deployment-config/chains/1.json b/deployment-config/chains/1.json index c9c214a..2f8ae36 100644 --- a/deployment-config/chains/1.json +++ b/deployment-config/chains/1.json @@ -53,12 +53,10 @@ }, "0x9ba021b0a9b958b5e75ce9f6dff97c7ee52cb3e6":{ "priceFeed": "0x19219bc90f48dee4d5cf202e09c438faacfd8bea", - "rateProvider": "0x0000000000000000000000000000000000000000", + "rateProvider": "0x47c2bDE6E8134BB69BE54ad09684B8CBBb57caC8", "decimals": 18, "description": "apxETH/ETH", "priceFeedType": 1 - } - } } \ No newline at end of file diff --git a/deployment-config/ssETH-L1.json b/deployment-config/ssETH-L1.json new file mode 100644 index 0000000..bc9e39e --- /dev/null +++ b/deployment-config/ssETH-L1.json @@ -0,0 +1,65 @@ +{ + "protocolAdmin": "0x0000000000417626Ef34D62C4DC189b021603f2F", + "base": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "boringVaultAndBaseDecimals": "18", + "boringVault": { + "boringVaultSalt": "0x1000000000000000000000000000000000000000000000000000000000000011", + "boringVaultName": "Sei Native Yield Super Seiyan ETH", + "boringVaultSymbol": "ssETH", + "address": "0x0000000000000000000000000000000000000000" + }, + "manager": { + "managerSalt": "0x2000000000000000000000000000000000000000000000000000000000000011", + "address": "0x0000000000000000000000000000000000000000" + }, + "accountant": { + "accountantSalt": "0x3000000000000000000000000000000000000000000000000000000000000011", + "payoutAddress": "0x0000000000417626Ef34D62C4DC189b021603f2F", + "allowedExchangeRateChangeUpper": "10030", + "allowedExchangeRateChangeLower": "9980", + "minimumUpdateDelayInSeconds": "3600", + "managementFee": "0", + "address": "0x0000000000000000000000000000000000000000" + }, + "teller": { + "tellerSalt": "0x4000000000000000000000000000000000000000000000000000000000000011", + "maxGasForPeer": 200000, + "minGasForPeer": 60000, + "peerEid": 30280, + "tellerContractName": "MultiChainLayerZeroTellerWithMultiAssetSupport", + "opMessenger": "0x0000000000000000000000000000000000000000", + "assets": [ + "0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee", + "0xbf5495efe5db9ce00f80364c8b423567e58d2110", + "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7", + "0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0", + "0xD9A442856C234a39a81a089C06451EBAa4306a72", + "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "0x9Ba021B0a9b958B5E75cE9f6dff97C7eE52cb3E6" + ], + "dvnIfNoDefault": { + "required": [ + "0x589dEDbD617e0CBcB916A9223F4d1300c294236b" + ], + "optional": [ + "0x380275805876Ff19055EA900CDb2B46a94ecF20D", + "0x8FafAE7Dd957044088b3d0F67359C327c6200d18", + "0xa59BA433ac34D2927232918Ef5B2eaAfcF130BA5", + "0xe552485d02EDd3067FE7FCbD4dd56BB1D3A998D2" + ], + "blockConfirmationsRequiredIfNoDefault": 15, + "optionalThreshold": 2 + }, + "address": "0x0000000000000000000000000000000000000000" + }, + "rolesAuthority": { + "rolesAuthoritySalt": "0x5000000000000000000000000000000000000000000000000000000000000011", + "strategist": "0x0000000000417626Ef34D62C4DC189b021603f2F", + "exchangeRateBot": "0x0000000000417626Ef34D62C4DC189b021603f2F", + "address": "0x0000000000000000000000000000000000000000" + }, + "decoder": { + "decoderSalt": "0x6000000000000000000000000000000000000000000000000000000000000007", + "address": "0x0000000000000000000000000000000000000000" + } +} \ No newline at end of file diff --git a/deployment-config/ssETH-L2.json b/deployment-config/ssETH-L2.json new file mode 100644 index 0000000..10d49d5 --- /dev/null +++ b/deployment-config/ssETH-L2.json @@ -0,0 +1,57 @@ +{ + "protocolAdmin": "0xF2dE1311C5b2C1BD94de996DA13F80010453e505", + "base": "0x160345fC359604fC6e70E3c5fAcbdE5F7A9342d8", + "boringVaultAndBaseDecimals": "18", + "boringVault": { + "boringVaultSalt": "0x1000000000000000000000000000000000000000000000000000000000000011", + "boringVaultName": "Sei Native Yield Super Seiyan ETH", + "boringVaultSymbol": "ssETH", + "address": "0x0000000000000000000000000000000000000000" + }, + "manager": { + "managerSalt": "0x2000000000000000000000000000000000000000000000000000000000000011", + "address": "0x0000000000000000000000000000000000000000" + }, + "accountant": { + "accountantSalt": "0x3000000000000000000000000000000000000000000000000000000000000011", + "payoutAddress": "0xF2dE1311C5b2C1BD94de996DA13F80010453e505", + "allowedExchangeRateChangeUpper": "10030", + "allowedExchangeRateChangeLower": "9980", + "minimumUpdateDelayInSeconds": "3600", + "managementFee": "0", + "address": "0x0000000000000000000000000000000000000000" + }, + "teller": { + "tellerSalt": "0x4000000000000000000000000000000000000000000000000000000000000011", + "maxGasForPeer": 200000, + "minGasForPeer": 60000, + "peerEid": 30101, + "tellerContractName": "MultiChainLayerZeroTellerWithMultiAssetSupport", + "opMessenger": "0x0000000000000000000000000000000000000000", + "assets": [], + "dvnIfNoDefault": { + "required": [ + "0x6788f52439aca6bff597d3eec2dc9a44b8fee842" + ], + "optional": [ + "0x1feb08b1a53a9710afce82d380b8c2833c69a37e", + "0x87048402c32632b7c4d0a892d82bc1160e8b2393", + "0xd24972c11f91c1bb9eaee97ec96bb9c33cf7af24", + "0xbd00c87850416db0995ef8030b104f875e1bdd15" + ], + "blockConfirmationsRequiredIfNoDefault": 15, + "optionalThreshold": 2 + }, + "address": "0x0000000000000000000000000000000000000000" + }, + "rolesAuthority": { + "rolesAuthoritySalt": "0x5000000000000000000000000000000000000000000000000000000000000011", + "strategist": "0xF2dE1311C5b2C1BD94de996DA13F80010453e505", + "exchangeRateBot": "0xF2dE1311C5b2C1BD94de996DA13F80010453e505", + "address": "0x0000000000000000000000000000000000000000" + }, + "decoder": { + "decoderSalt": "0x6000000000000000000000000000000000000000000000000000000000000007", + "address": "0x0000000000000000000000000000000000000000" + } +} \ No newline at end of file diff --git a/lzConfigCheck.cjs b/lzConfigCheck.cjs index ded3c98..b1e813e 100644 --- a/lzConfigCheck.cjs +++ b/lzConfigCheck.cjs @@ -1,3 +1,5 @@ +import { log } from 'console'; + const fs = require('fs'); const readline = require('readline'); @@ -112,11 +114,17 @@ async function main() { } const chain2 = findings2.findings[0].chain; + const providers2 = findings2.findings.map(finding => finding.provider); for (const finding of findings2.findings) { assert(providers1.includes(finding.provider), "Provider: "+finding.provider+" does not have a matching provider in the first config"); assert(finding.chain == chain2, "Networks do not match for: "+finding); } + console.log('chain1', chain1); + console.log('providers1', providers1); + console.log('chain2', chain2); + console.log('providers2', providers2); + console.log("✅ Config check passed"); } catch (error) { console.error(error.message); diff --git a/test/LiveDeploy.t.sol b/test/LiveDeploy.t.sol index 1027477..fe768ca 100644 --- a/test/LiveDeploy.t.sol +++ b/test/LiveDeploy.t.sol @@ -4,6 +4,7 @@ pragma solidity 0.8.21; import { Test, stdStorage, StdStorage, stdError, console } from "@forge-std/Test.sol"; import { DeployAll } from "script/deploy/deployAll.s.sol"; import { ConfigReader } from "script/ConfigReader.s.sol"; +import { SOLVER_ROLE } from "script/deploy/single/06_DeployRolesAuthority.s.sol"; import { ERC20 } from "@solmate/tokens/ERC20.sol"; import { BoringVault } from "src/base/BoringVault.sol"; import { FixedPointMathLib } from "@solmate/utils/FixedPointMathLib.sol"; @@ -23,6 +24,8 @@ import { CrossChainOPTellerWithMultiAssetSupport } from import { MultiChainLayerZeroTellerWithMultiAssetSupport } from "src/base/Roles/CrossChain/MultiChainLayerZeroTellerWithMultiAssetSupport.sol"; +import { console2 } from "forge-std/console2.sol"; + string constant DEFAULT_RPC_URL = "L1_RPC_URL"; uint256 constant DELTA = 10_000; @@ -49,7 +52,6 @@ contract LiveDeploy is ForkTest, DeployAll { ERC20 constant NATIVE_ERC20 = ERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); uint256 ONE_SHARE; - uint8 constant SOLVER_ROLE = 42; function setUp() public virtual { string memory FILE_NAME; @@ -87,9 +89,6 @@ contract LiveDeploy is ForkTest, DeployAll { assertNotEq(rateProvider.code.length, 0, "No code at rate provider address"); } - // warp forward the minimumUpdateDelay for the accountant to prevent it from pausing on update test - vm.warp(block.timestamp + mainConfig.minimumUpdateDelayInSeconds); - // define one share based off of vault decimals ONE_SHARE = 10 ** BoringVault(payable(mainConfig.boringVault)).decimals(); @@ -211,7 +210,7 @@ contract LiveDeploy is ForkTest, DeployAll { ? mainConfig.allowedExchangeRateChangeLower + 1 : rateChange; - depositAmount = bound(depositAmount, 1, 10_000e18); + depositAmount = bound(depositAmount, 0.5e18, 10_000e18); // mint a bunch of extra tokens to the vault for if rate increased deal(mainConfig.base, mainConfig.boringVault, depositAmount); @@ -223,7 +222,6 @@ contract LiveDeploy is ForkTest, DeployAll { expectedSharesByAsset[i] = depositAmount.mulDivDown(ONE_SHARE, accountant.getRateInQuoteSafe(ERC20(mainConfig.assets[i]))); expecteShares += expectedSharesByAsset[i]; - _depositAssetWithApprove(ERC20(mainConfig.assets[i]), depositAmount); } @@ -243,7 +241,7 @@ contract LiveDeploy is ForkTest, DeployAll { ); // mint extra assets for vault to give out - deal(mainConfig.assets[i], mainConfig.boringVault, depositAmount); + deal(mainConfig.assets[i], mainConfig.boringVault, depositAmount * 2); uint256 expectedAssetsBack = ((depositAmount) * rateChange / 10_000); @@ -417,9 +415,13 @@ contract LiveDeploy is ForkTest, DeployAll { function _updateRate(uint96 rateChange, AccountantWithRateProviders accountant) internal { // update the rate + // warp forward the minimumUpdateDelay for the accountant to prevent it from pausing on update test + uint256 time = block.timestamp; + vm.warp(time + mainConfig.minimumUpdateDelayInSeconds); vm.startPrank(mainConfig.exchangeRateBot); uint96 newRate = uint96(accountant.getRate()) * rateChange / 10_000; accountant.updateExchangeRate(newRate); vm.stopPrank(); + vm.warp(time); } } From 249aab55d00f7b7e6132f945f73151bb6b892ba1 Mon Sep 17 00:00:00 2001 From: Jun Kim <64379343+junkim012@users.noreply.github.com> Date: Wed, 11 Sep 2024 20:55:24 -0400 Subject: [PATCH 2/4] feat: the rate provider deployment script deploys either generic, chainlink, or redstone --- deployment-config/chains/1.json | 125 ++++++++++++------- deployment-config/exampleL1.json | 7 ++ deployment-config/ssETH-L1.json | 3 +- script/deploy/01_DeployRateProviders.s.sol | 133 ++++++++++++++++----- 4 files changed, 197 insertions(+), 71 deletions(-) diff --git a/deployment-config/chains/1.json b/deployment-config/chains/1.json index 2f8ae36..c8f6327 100644 --- a/deployment-config/chains/1.json +++ b/deployment-config/chains/1.json @@ -4,59 +4,98 @@ "opMessenger": "0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1", "lzEndpoint": "0x1a44076050125825900e736c501f859c50fE728c", "assetToRateProviderAndPriceFeed": { + "NOTE_0_CHAINLINK_1_REDSTONE_2_GENERIC": 0, "NOTE_THESE_KEYS_MUST_NOT_BE_CHECKSUMMED": 0, + "maxTimeFromLastUpdate": "86400", + "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0": { + "denomination": "stETH/wstETH", + "priceFeedType": 2, + "target": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + "signature": "stEthPerToken()", + "arg": 0, + "expectedMin": "1000000000000000000", + "expectedMax": "1200000000000000000", + "rateProvider": "0xf7c9c121b09cd45591554eb8419a4e8a47e7b0a8" + }, "0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee": { - "priceFeed": "0x5c9C449BbC9a6075A2c061dF312a35fd1E05fF22", - "rateProvider": "0x51a9319B45F41c405eFA1EcbCcE530607A694862", - "decimals": 18, - "description": "weETH / ETH", - "priceFeedType": 0 + "denomination": "eeETH/weETH", + "priceFeedType": 2, + "target": "0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee", + "signature": "getEETHByWeETH(uint256)", + "arg": 1000000000000000000, + "expectedMin": "1000000000000000000", + "expectedMax": "1200000000000000000", + "rateProvider": "0x7d3b0ce57842b01abf6c490646fbb694dfa389e4" }, "0xbf5495efe5db9ce00f80364c8b423567e58d2110": { - "priceFeed": "0x636A000262F6aA9e1F094ABF0aD8f645C44f641C", - "rateProvider": "0xb9B1eD92d3902eBEca23F931a629343D4C46c38D", - "decimals": 18, - "description": "ezETH / ETH", - "priceFeedType": 0 - }, - "0xa1290d69c65a6fe4df752f95823fae25cb99e5a7":{ - "priceFeed": "0x03c68933f7a3F76875C0bc670a58e69294cDFD01", - "rateProvider": "0xB140e881E206E94B18e143f1739d5f212C59C859", - "decimals": 18, - "description": "RSETH / ETH", - "priceFeedType": 0 + "denomination": "ETH/ezETH", + "priceFeedType": 2, + "target": "0x3239396B740cD6BBABb42196A03f7B77fA7102C9", + "signature": "getProtocolExchangeRate()", + "arg": 0, + "expectedMin": "1000000000000000000", + "expectedMax": "1200000000000000000", + "rateProvider": "0x0852be00fa37fc24fb34111e3a4e44a28fb76106" + }, + "0xa1290d69c65a6fe4df752f95823fae25cb99e5a7": { + "denomination": "ETH/rsETH", + "priceFeedType": 2, + "target": "0x349A73444b1a310BAe67ef67973022020d70020d", + "signature": "rsETHPrice()", + "arg": 0, + "expectedMin": "1000000000000000000", + "expectedMax": "1200000000000000000", + "rateProvider": "0x6aeea90872fcfb5a45befd070adc3fcd8e71c067" }, "0xfae103dc9cf190ed75350761e95403b7b8afa6c0": { - "priceFeed": "0xb613CfebD0b6e95abDDe02677d6bC42394FdB857", - "rateProvider": "0x4E323185C91Ef73e36f0CD2DdDcE54fb33112949", - "decimals": 18, - "description": "rswETH / ETH", - "priceFeedType": 0 + "denomination": "ETH/rswETH", + "priceFeedType": 2, + "target": "0xfae103dc9cf190ed75350761e95403b7b8afa6c0", + "signature": "getRate()", + "arg": 0, + "expectedMin": "1000000000000000000", + "expectedMax": "1200000000000000000", + "rateProvider": "0x99554bbcb88c2a26897e77686ee5425cebfb4f01" }, "0xd9a442856c234a39a81a089c06451ebaa4306a72": { - "priceFeed": "0x76A495b0bFfb53ef3F0E94ef0763e03cE410835C", - "rateProvider": "0xEE8259373fCf5dc12F511867551F7f6FA6fcb0FB", - "decimals": 18, - "description": "pufETH/ETH", - "priceFeedType": 1 - }, - "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0": { - "priceFeed": "0x86392dC19c0b719886221c78AB11eb8Cf5c52812", - "rateProvider": "0xa360Df495d0560bDDc5d681B54991629965ae170", - "decimals": 18, - "description": "STETH / ETH", - "priceFeedType": 0 + "denomination": "ETH/pufETH", + "priceFeedType": 2, + "target": "0xd9a442856c234a39a81a089c06451ebaa4306a72", + "signature": "previewRedeem(uint256)", + "arg": 1000000000000000000, + "expectedMin": "1000000000000000000", + "expectedMax": "1200000000000000000", + "rateProvider": "0xbda3cfa3be083f4087cb3b647da8dfca51bdaa6a" }, "0x8db2350d78abc13f5673a411d4700bcf87864dde": { - "rateProvider": "0x318Da095d602C08eF41319f4c4bA0646d318C906", - "decimals": 8 - }, - "0x9ba021b0a9b958b5e75ce9f6dff97c7ee52cb3e6":{ - "priceFeed": "0x19219bc90f48dee4d5cf202e09c438faacfd8bea", - "rateProvider": "0x47c2bDE6E8134BB69BE54ad09684B8CBBb57caC8", - "decimals": 18, - "description": "apxETH/ETH", - "priceFeedType": 1 + "denomination": "WBTC/swBTC", + "priceFeedType": 2, + "target": "0x8DB2350D78aBc13f5673A411D4700BCF87864dDE", + "signature": "pricePerShare()", + "arg": 0, + "expectedMin": "100000000", + "expectedMax": "100000000", + "rateProvider": "0x318Da095d602C08eF41319f4c4bA0646d318C906" + }, + "0x9ba021b0a9b958b5e75ce9f6dff97c7ee52cb3e6": { + "denomination": "ETH/apxETH", + "priceFeedType": 2, + "target": "0x9ba021b0a9b958b5e75ce9f6dff97c7ee52cb3e6", + "signature": "assetsPerShare()", + "arg": 0, + "expectedMin": "1000000000000000000", + "expectedMax": "1200000000000000000", + "rateProvider": "0x9a044a83ddd7de8cafd8ecbf70bf7dad4865cf44" + }, + "0xac3e018457b222d93114458476f3e3416abbe38f": { + "denomination": "ETH/sfrxETH", + "priceFeedType": 2, + "target": "0xac3e018457b222d93114458476f3e3416abbe38f", + "signature": "pricePerShare()", + "arg": 0, + "expectedMin": "1000000000000000000", + "expectedMax": "1200000000000000000", + "rateProvider": "0xa427b23b686986ed993b4ba9ae23bf65022f938a" } } } \ No newline at end of file diff --git a/deployment-config/exampleL1.json b/deployment-config/exampleL1.json index 72e92b3..412064b 100644 --- a/deployment-config/exampleL1.json +++ b/deployment-config/exampleL1.json @@ -28,6 +28,13 @@ "peerEid": 30280, "tellerContractName": "TellerWithMultiAssetSupport", "assets": [ + "0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee", + "0xbf5495efe5db9ce00f80364c8b423567e58d2110", + "0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7", + "0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0", + "0xD9A442856C234a39a81a089C06451EBAa4306a72", + "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + "0x9Ba021B0a9b958B5E75cE9f6dff97C7eE52cb3E6" ], "dvnIfNoDefault": { "required": [ diff --git a/deployment-config/ssETH-L1.json b/deployment-config/ssETH-L1.json index bc9e39e..54c9afb 100644 --- a/deployment-config/ssETH-L1.json +++ b/deployment-config/ssETH-L1.json @@ -35,7 +35,8 @@ "0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0", "0xD9A442856C234a39a81a089C06451EBAa4306a72", "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", - "0x9Ba021B0a9b958B5E75cE9f6dff97C7eE52cb3E6" + "0x9Ba021B0a9b958B5E75cE9f6dff97C7eE52cb3E6", + "0xac3E018457B222d93114458476f3E3416Abbe38F" ], "dvnIfNoDefault": { "required": [ diff --git a/script/deploy/01_DeployRateProviders.s.sol b/script/deploy/01_DeployRateProviders.s.sol index 363a7cb..4d13e97 100644 --- a/script/deploy/01_DeployRateProviders.s.sol +++ b/script/deploy/01_DeployRateProviders.s.sol @@ -1,15 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.21; -import { IRateProvider } from "./../../../src/interfaces/IRateProvider.sol"; -import { EthPerWstEthRateProvider } from "./../../../src/oracles/EthPerWstEthRateProvider.sol"; - +import { GenericRateProvider } from "./../../../src/helper/GenericRateProvider.sol"; import { BaseScript } from "./../Base.s.sol"; import { stdJson as StdJson } from "@forge-std/StdJson.sol"; import { ConfigReader } from "../ConfigReader.s.sol"; -import { console } from "@forge-std/Test.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { EthPerTokenRateProvider, IPriceFeed } from "src/oracles/EthPerTokenRateProvider.sol"; +import { console2 } from "forge-std/console2.sol"; /// NOTE This script must change based on the supported assets of each vault deployment. contract DeployRateProviders is BaseScript { @@ -27,60 +25,141 @@ contract DeployRateProviders is BaseScript { _run(Strings.toString(block.chainid), config); } + /** + * For the assets specified in the teller, look at .json, and if + * the rate provider does not exist, either deploy Chainlink, Redstone, or + * the Generic Rate Provider. + */ function _run(string memory fileName, string memory config) internal { string memory chainConfig = getChainConfigFile(); address[] memory assets = config.readAddressArray(".teller.assets"); - uint256 maxTimeFromLastUpdate = config.readUint(".rateProvider.maxTimeFromLastUpdate"); + uint256 maxTimeFromLastUpdate = chainConfig.readUint(".assetToRateProviderAndPriceFeed.maxTimeFromLastUpdate"); for (uint256 i; i < assets.length; ++i) { require(assets[i].code.length > 0, "asset must have code"); string memory rateProviderKey = string(abi.encodePacked(".assetToRateProviderAndPriceFeed.", assets[i].toHexString(), ".rateProvider")); + + string memory token = assets[i].toHexString(); + console2.log("token: ", token); + address rateProvider = chainConfig.readAddress(rateProviderKey); + // must deploy new rate provider and set the value if (rateProvider == address(0)) { - address priceFeed = chainConfig.readAddress( - string(abi.encodePacked(".assetToRateProviderAndPriceFeed.", assets[i].toHexString(), ".priceFeed")) + address deployedAddress; + + uint256 priceFeedType = chainConfig.readUint( + string(abi.encodePacked(".assetToRateProviderAndPriceFeed.", token, ".priceFeedType")) ); - uint8 decimals = uint8( - chainConfig.readUint( + + // Generic Rate Provider + if (priceFeedType == 2) { + address target = chainConfig.readAddress( + string(abi.encodePacked(".assetToRateProviderAndPriceFeed.", token, ".target")) + ); + + string memory signature = chainConfig.readString( + string(abi.encodePacked(".assetToRateProviderAndPriceFeed.", token, ".signature")) + ); + + uint256 arg = chainConfig.readUint( + string(abi.encodePacked(".assetToRateProviderAndPriceFeed.", token, ".arg")) + ); + + uint256 expectedMin = chainConfig.readUint( + string(abi.encodePacked(".assetToRateProviderAndPriceFeed.", token, ".expectedMin")) + ); + + uint256 expectedMax = chainConfig.readUint( + string(abi.encodePacked(".assetToRateProviderAndPriceFeed.", token, ".expectedMax")) + ); + + deployedAddress = _deployGenericRateProvider(target, signature, arg, expectedMin, expectedMax); + } else { + // Chainlink or Redstone + address priceFeed = chainConfig.readAddress( string( - abi.encodePacked(".assetToRateProviderAndPriceFeed.", assets[i].toHexString(), ".decimals") + abi.encodePacked(".assetToRateProviderAndPriceFeed.", assets[i].toHexString(), ".priceFeed") ) - ) - ); - string memory description = chainConfig.readString( - string( - abi.encodePacked(".assetToRateProviderAndPriceFeed.", assets[i].toHexString(), ".description") - ) - ); - uint256 priceFeedType = chainConfig.readUint( - string( - abi.encodePacked(".assetToRateProviderAndPriceFeed.", assets[i].toHexString(), ".priceFeedType") - ) - ); - rateProvider = - deployRateProvider(description, priceFeed, maxTimeFromLastUpdate, decimals, priceFeedType); + ); + uint8 decimals = uint8( + chainConfig.readUint( + string( + abi.encodePacked( + ".assetToRateProviderAndPriceFeed.", assets[i].toHexString(), ".decimals" + ) + ) + ) + ); + string memory description = chainConfig.readString( + string( + abi.encodePacked( + ".assetToRateProviderAndPriceFeed.", assets[i].toHexString(), ".description" + ) + ) + ); + uint256 priceFeedType = chainConfig.readUint( + string( + abi.encodePacked( + ".assetToRateProviderAndPriceFeed.", assets[i].toHexString(), ".priceFeedType" + ) + ) + ); + deployedAddress = + _deployRateProvider(description, priceFeed, maxTimeFromLastUpdate, decimals, priceFeedType); + } string memory chainConfigFilePath = string.concat(CONFIG_CHAIN_ROOT, fileName, ".json"); - rateProvider.toHexString().write(chainConfigFilePath, rateProviderKey); + deployedAddress.toHexString().write(chainConfigFilePath, rateProviderKey); + console2.log("deployedAddress: ", deployedAddress); } } } function deploy(ConfigReader.Config memory config) public override broadcast returns (address) { } - function deployRateProvider( + function _deployGenericRateProvider( + address target, + string memory signature, + uint256 arg, + uint256 expectedMin, + uint256 expectedMax + ) + internal + broadcast + returns (address) + { + bytes4 functionSig = bytes4(keccak256(bytes(signature))); + + bytes memory creationCode = type(GenericRateProvider).creationCode; + + GenericRateProvider rateProvider = + new GenericRateProvider(target, functionSig, bytes32(arg), 0, 0, 0, 0, 0, 0, 0); + + uint256 rate = rateProvider.getRate(); + + console2.log("rate: ", rate); + + require(rate != 0, "rate must not be zero"); + require(rate >= expectedMin, "rate must be greater than or equal to min"); + require(rate <= expectedMax, "rate must be less than or equal to max"); + + return address(rateProvider); + } + + function _deployRateProvider( string memory description, address priceFeed, uint256 maxTimeFromLastUpdate, uint8 decimals, uint256 priceFeedType ) - public + internal broadcast returns (address) { + console2.log("priceFeed: ", priceFeed); require(maxTimeFromLastUpdate > 0, "max time from last update = 0"); require(priceFeed.code.length > 0, "price feed must have code"); From 280ba30234b82d0286cbbddc38e4ce80964506a7 Mon Sep 17 00:00:00 2001 From: Jun Kim <64379343+junkim012@users.noreply.github.com> Date: Wed, 18 Sep 2024 22:55:18 -0400 Subject: [PATCH 3/4] feat: generic rate provider for seiyanETH on Sei Network to add seiyanETH as a teller supported asset --- Makefile | 2 +- deployment-config/chains/1329.json | 17 ++- deployment-config/exampleL1.json | 5 +- deployment-config/exampleL2.json | 5 +- deployment-config/ssETH-L2.json | 4 +- script/ConfigReader.s.sol | 2 + script/deploy/01_DeployRateProviders.s.sol | 2 - script/deploy/DeployAtomicQueue.s.sol | 18 +++ .../single/06_DeployRolesAuthority.s.sol | 106 +++++++++++++++--- 9 files changed, 139 insertions(+), 22 deletions(-) create mode 100644 script/deploy/DeployAtomicQueue.s.sol diff --git a/Makefile b/Makefile index 3ded71e..5320a3e 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ live-deployL1: live-deployL2: @echo "Setting environment variable LIVE_DEPLOY_READ_FILE_NAME to $(file)" - @export LIVE_DEPLOY_READ_FILE_NAME=$(file) && forge script script/deploy/deployAll.s.sol --sig "run(string)" $(file) --fork-url=${L2_RPC_URL} --private-key=$(PRIVATE_KEY) --broadcast --slow + @export LIVE_DEPLOY_READ_FILE_NAME=$(file) && forge script script/deploy/deployAll.s.sol --sig "run(string)" $(file) --fork-url=${L2_RPC_URL} --private-key=$(PRIVATE_KEY) --broadcast --slow --verify prettier: prettier --write '**/*.{md,yml,yaml,ts,js}' diff --git a/deployment-config/chains/1329.json b/deployment-config/chains/1329.json index 4596379..ee6af86 100644 --- a/deployment-config/chains/1329.json +++ b/deployment-config/chains/1329.json @@ -1,5 +1,20 @@ { "name": "Sei", "balancerVault": "0x0000000000000000000000000000000000000000", - "lzEndpoint": "0x1a44076050125825900e736c501f859c50fE728c" + "lzEndpoint": "0x1a44076050125825900e736c501f859c50fE728c", + "assetToRateProviderAndPriceFeed": { + "NOTE_0_CHAINLINK_1_REDSTONE_2_GENERIC": 0, + "NOTE_THESE_KEYS_MUST_NOT_BE_CHECKSUMMED": 0, + "maxTimeFromLastUpdate": "86400", + "0x9faaea2cdd810b21594e54309dc847842ae301ce": { + "denomination": "ETH/seiyanETH", + "priceFeedType": 2, + "target": "0x24152894Decc7384b05E8907D6aDAdD82c176499", + "signature": "getRateSafe()", + "arg": 0, + "expectedMin": "1000000000000000000", + "expectedMax": "1100000000000000000", + "rateProvider": "0x4cb84449fab2812556533491a5c171611a9737fa" + } + } } \ No newline at end of file diff --git a/deployment-config/exampleL1.json b/deployment-config/exampleL1.json index 412064b..fec5e88 100644 --- a/deployment-config/exampleL1.json +++ b/deployment-config/exampleL1.json @@ -53,8 +53,9 @@ }, "rolesAuthority": { "rolesAuthoritySalt": "0x66bbc3b3b3000b01466a3a00000000000000000000000000000000000000001c", - "strategist": "0xC2d99d76bb9D46BF8Ec9449E4DfAE48C30CF0839", - "exchangeRateBot": "0x00000000004F96C07B83e86600D86F0000000000", + "strategist": "0x00000000004F96C07B83e86600D86F0000000000", + "exchangeRateBot": "0x1755397BEc366a1e1160d8aE0106C60E7e344B56", + "pauser": "0xe5CcB29Cb9C886da329098A184302E2D5Ff0cD9E", "address": "0x00000000004F96C07B83e86600D86F0000000000" }, "decoder": { diff --git a/deployment-config/exampleL2.json b/deployment-config/exampleL2.json index 166ff3a..c5eff07 100644 --- a/deployment-config/exampleL2.json +++ b/deployment-config/exampleL2.json @@ -45,8 +45,9 @@ }, "rolesAuthority": { "rolesAuthoritySalt": "0x66bbc3b3b3000b01466a3a00000000000000000000000000000000000000000e", - "strategist": "0xC2d99d76bb9D46BF8Ec9449E4DfAE48C30CF0839", - "exchangeRateBot": "0x00000000004F96C07B83e86600D86F0000000000", + "strategist": "0x00000000004F96C07B83e86600D86F0000000000", + "exchangeRateBot": "0x1755397BEc366a1e1160d8aE0106C60E7e344B56", + "pauser": "0xe5CcB29Cb9C886da329098A184302E2D5Ff0cD9E", "address": "0x00000000004F96C07B83e86600D86F0000000000" }, "decoder": { diff --git a/deployment-config/ssETH-L2.json b/deployment-config/ssETH-L2.json index 10d49d5..ac7c4d9 100644 --- a/deployment-config/ssETH-L2.json +++ b/deployment-config/ssETH-L2.json @@ -28,7 +28,9 @@ "peerEid": 30101, "tellerContractName": "MultiChainLayerZeroTellerWithMultiAssetSupport", "opMessenger": "0x0000000000000000000000000000000000000000", - "assets": [], + "assets": [ + "0x9fAaEA2CDd810b21594E54309DC847842Ae301Ce" + ], "dvnIfNoDefault": { "required": [ "0x6788f52439aca6bff597d3eec2dc9a44b8fee842" diff --git a/script/ConfigReader.s.sol b/script/ConfigReader.s.sol index 9e8a74c..e19a516 100644 --- a/script/ConfigReader.s.sol +++ b/script/ConfigReader.s.sol @@ -45,6 +45,7 @@ library ConfigReader { string tellerContractName; address strategist; address exchangeRateBot; + address pauser; address rolesAuthority; bytes32 decoderSalt; address decoder; @@ -100,6 +101,7 @@ library ConfigReader { config.rolesAuthoritySalt = _config.readBytes32(".rolesAuthority.rolesAuthoritySalt"); config.strategist = _config.readAddress(".rolesAuthority.strategist"); config.exchangeRateBot = _config.readAddress(".rolesAuthority.exchangeRateBot"); + config.pauser = _config.readAddress(".rolesAuthority.pauser"); // Reading from the 'decoder' section config.decoderSalt = _config.readBytes32(".decoder.decoderSalt"); diff --git a/script/deploy/01_DeployRateProviders.s.sol b/script/deploy/01_DeployRateProviders.s.sol index 4d13e97..16f8b01 100644 --- a/script/deploy/01_DeployRateProviders.s.sol +++ b/script/deploy/01_DeployRateProviders.s.sol @@ -132,8 +132,6 @@ contract DeployRateProviders is BaseScript { { bytes4 functionSig = bytes4(keccak256(bytes(signature))); - bytes memory creationCode = type(GenericRateProvider).creationCode; - GenericRateProvider rateProvider = new GenericRateProvider(target, functionSig, bytes32(arg), 0, 0, 0, 0, 0, 0, 0); diff --git a/script/deploy/DeployAtomicQueue.s.sol b/script/deploy/DeployAtomicQueue.s.sol new file mode 100644 index 0000000..29aba4d --- /dev/null +++ b/script/deploy/DeployAtomicQueue.s.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.21; + +import { AtomicQueue } from "./../../src/atomic-queue/AtomicQueue.sol"; +import { BaseScript } from "../Base.s.sol"; +import { stdJson as StdJson } from "@forge-std/StdJson.sol"; + +using StdJson for string; + +bytes32 constant SALT = 0x5bac910c72debe007de61c000000000000000000000000000000000000000000; + +contract DeployAtomicQueue is BaseScript { + function run() public broadcast returns (AtomicQueue atomicQueue) { + bytes memory creationCode = type(AtomicQueue).creationCode; + + atomicQueue = AtomicQueue(CREATEX.deployCreate3(SALT, creationCode)); + } +} diff --git a/script/deploy/single/06_DeployRolesAuthority.s.sol b/script/deploy/single/06_DeployRolesAuthority.s.sol index b2a2352..9c389f4 100644 --- a/script/deploy/single/06_DeployRolesAuthority.s.sol +++ b/script/deploy/single/06_DeployRolesAuthority.s.sol @@ -16,6 +16,7 @@ uint8 constant MANAGER_ROLE = 2; uint8 constant TELLER_ROLE = 3; uint8 constant UPDATE_EXCHANGE_RATE_ROLE = 4; uint8 constant SOLVER_ROLE = 5; +uint8 constant PAUSER_ROLE = 6; /** * NOTE Deploys with `Authority` set to zero bytes. @@ -55,10 +56,6 @@ contract DeployRolesAuthority is BaseScript { ); // Setup initial roles configurations - // --- Users --- - // 1. VAULT_STRATEGIST (BOT EOA) - // 2. MANAGER (CONTRACT) - // 3. TELLER (CONTRACT) // --- Roles --- // 1. STRATEGIST_ROLE // - manager.manageVaultWithMerkleVerification @@ -70,8 +67,22 @@ contract DeployRolesAuthority is BaseScript { // - boringVault.enter() // - boringVault.exit() // - assigned to TELLER - // --- Public --- - // 1. teller.deposit + // 4. PAUSER_ROLE + // - teller.pause() + // - accountant.pause() + // - manager.pause() + // --- Public Functions --- + // 1. teller.deposit() + // 2. teller.bridge() + // 3. teller.depositAndBridge() + // --- Users / Role Assignments --- + // STRATEGIST_ROLE -> OWNER (multisig) + // MANAGER_ROLE -> MANAGER (contract) + // TELLER_ROLE -> TELLER (contract) + // UPDATE_EXCHANGE_RATE_ROLE -> EXCHANGE_RATE_BOT (EOA) & OWNER (multisig) + // PAUSER_ROLE -> PAUSER (EOA) & OWNER (multisig) + + // --- Set Role Capabilities --- rolesAuthority.setRoleCapability( STRATEGIST_ROLE, config.manager, @@ -94,14 +105,23 @@ contract DeployRolesAuthority is BaseScript { rolesAuthority.setRoleCapability(TELLER_ROLE, config.boringVault, BoringVault.exit.selector, true); - rolesAuthority.setPublicCapability(config.teller, TellerWithMultiAssetSupport.deposit.selector, true); - rolesAuthority.setPublicCapability(config.teller, CrossChainTellerBase.bridge.selector, true); - rolesAuthority.setPublicCapability(config.teller, CrossChainTellerBase.depositAndBridge.selector, true); - rolesAuthority.setRoleCapability( UPDATE_EXCHANGE_RATE_ROLE, config.accountant, AccountantWithRateProviders.updateExchangeRate.selector, true ); + rolesAuthority.setRoleCapability(PAUSER_ROLE, config.teller, TellerWithMultiAssetSupport.pause.selector, true); + rolesAuthority.setRoleCapability( + PAUSER_ROLE, config.accountant, AccountantWithRateProviders.pause.selector, true + ); + rolesAuthority.setRoleCapability( + PAUSER_ROLE, config.manager, ManagerWithMerkleVerification.pause.selector, true + ); + + // --- Set Public Capabilities --- + rolesAuthority.setPublicCapability(config.teller, TellerWithMultiAssetSupport.deposit.selector, true); + rolesAuthority.setPublicCapability(config.teller, CrossChainTellerBase.bridge.selector, true); + rolesAuthority.setPublicCapability(config.teller, CrossChainTellerBase.depositAndBridge.selector, true); + // --- Assign roles to users --- rolesAuthority.setUserRole(config.strategist, STRATEGIST_ROLE, true); @@ -110,19 +130,45 @@ contract DeployRolesAuthority is BaseScript { rolesAuthority.setUserRole(config.teller, TELLER_ROLE, true); - rolesAuthority.setUserRole(config.exchangeRateBot, UPDATE_EXCHANGE_RATE_ROLE, true); + rolesAuthority.setUserRole(config.protocolAdmin, UPDATE_EXCHANGE_RATE_ROLE, true); + if (config.exchangeRateBot != address(0)) { + rolesAuthority.setUserRole(config.exchangeRateBot, UPDATE_EXCHANGE_RATE_ROLE, true); + } + + rolesAuthority.setUserRole(config.protocolAdmin, PAUSER_ROLE, true); + if (config.pauser != address(0)) { + rolesAuthority.setUserRole(config.pauser, PAUSER_ROLE, true); + } // Post Deploy Checks require( rolesAuthority.doesUserHaveRole(config.strategist, STRATEGIST_ROLE), "strategist should have STRATEGIST_ROLE" ); + require(rolesAuthority.doesUserHaveRole(config.manager, MANAGER_ROLE), "manager should have MANAGER_ROLE"); + require(rolesAuthority.doesUserHaveRole(config.teller, TELLER_ROLE), "teller should have TELLER_ROLE"); + require( - rolesAuthority.doesUserHaveRole(config.exchangeRateBot, UPDATE_EXCHANGE_RATE_ROLE), - "exchangeRateBot should have UPDATE_EXCHANGE_RATE_ROLE" + rolesAuthority.doesUserHaveRole(config.protocolAdmin, UPDATE_EXCHANGE_RATE_ROLE), + "protocolAdmin should have UPDATE_EXCHANGE_RATE_ROLE" ); + require( + rolesAuthority.doesUserHaveRole(config.protocolAdmin, PAUSER_ROLE), "protocolAdmin should have PAUSER_ROLE" + ); + + if (config.exchangeRateBot != address(0)) { + require( + rolesAuthority.doesUserHaveRole(config.exchangeRateBot, UPDATE_EXCHANGE_RATE_ROLE), + "exchangeRateBot should have UPDATE_EXCHANGE_RATE_ROLE" + ); + } + + if (config.pauser != address(0)) { + require(rolesAuthority.doesUserHaveRole(config.pauser, PAUSER_ROLE), "pauser should have PAUSER_ROLE"); + } + require( rolesAuthority.canCall( config.strategist, @@ -159,11 +205,45 @@ contract DeployRolesAuthority is BaseScript { ), "exchangeRateBot should be able to call accountant.updateExchangeRate" ); + require( + rolesAuthority.canCall( + config.protocolAdmin, config.accountant, AccountantWithRateProviders.updateExchangeRate.selector + ), + "protocolAdmin should be able to call accountant.updateExchangeRate" + ); require( rolesAuthority.canCall(address(1), config.teller, TellerWithMultiAssetSupport.deposit.selector), "anyone should be able to call teller.deposit" ); + // Pauser Roles + _validatePauserRole(config, rolesAuthority, config.protocolAdmin); + if (config.pauser != address(0)) { + _validatePauserRole(config, rolesAuthority, config.pauser); + } + return address(rolesAuthority); } + + function _validatePauserRole( + ConfigReader.Config memory config, + RolesAuthority rolesAuthority, + address user + ) + internal + view + { + require( + rolesAuthority.canCall(user, config.teller, TellerWithMultiAssetSupport.pause.selector), + "pauser should be able to call teller.pause" + ); + require( + rolesAuthority.canCall(user, config.accountant, AccountantWithRateProviders.pause.selector), + "pauser should be able to call accountant.pause" + ); + require( + rolesAuthority.canCall(user, config.manager, ManagerWithMerkleVerification.pause.selector), + "pauser should be able to call manager.pause" + ); + } } From f1a381ac9c1a067e95ff2de368ba86ad698c13a0 Mon Sep 17 00:00:00 2001 From: Jun Kim <64379343+junkim012@users.noreply.github.com> Date: Thu, 19 Sep 2024 01:22:33 -0400 Subject: [PATCH 4/4] fix: check-configs takes in input arguments --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5320a3e..216a343 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ include .env check-configs: - bun lzConfigCheck.cjs + @echo "l1_file: ${l1_file} l2_file ${l2_file}" + bun lzConfigCheck.cjs ${l1_file} ${l2_file} checkL1: @echo "Setting environment variable LIVE_DEPLOY_READ_FILE_NAME to $(file)"