diff --git a/.github/workflows/deploy-market.yaml b/.github/workflows/deploy-market.yaml index 6889852d6..783c7cff4 100644 --- a/.github/workflows/deploy-market.yaml +++ b/.github/workflows/deploy-market.yaml @@ -16,6 +16,7 @@ on: - arbitrum-goerli - base - base-goerli + - base-sepolia - linea-goerli - scroll-goerli - scroll @@ -42,11 +43,11 @@ jobs: LINEASCAN_KEY: ${{ secrets.LINEASCAN_KEY }} steps: - name: Seacrest - uses: hayesgm/seacrest@5748b3a066f517973ca2ca03d0af39bbf2b82d10 + uses: hayesgm/seacrest@1dc2d6e3750d5f5589173555220695a09b913071 with: wallet_connect_project_id: ${{ secrets.WALLET_CONNECT_PROJECT_ID }} requested_network: "${{ inputs.network }}" - ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"mainnet\":\"https://mainnet.infura.io/v3/$INFURA_KEY\",\"goerli\":\"https://goerli.infura.io/v3/$INFURA_KEY\",\"sepolia\":\"https://sepolia.infura.io/v3/$INFURA_KEY\",\"mumbai\":\"https://polygon-mumbai.infura.io/v3/$INFURA_KEY\",\"polygon\":\"https://polygon-mainnet.infura.io/v3/$INFURA_KEY\",\"arbitrum-goerli\":\"https://arbitrum-goerli.infura.io/v3/$INFURA_KEY\",\"arbitrum\":\"https://arbitrum-mainnet.infura.io/v3/$INFURA_KEY\",\"base\":\"https://clean-spring-wind.base-mainnet.discover.quiknode.pro/$QUICKNODE_KEY\",\"base-goerli\":\"https://base-goerli.infura.io/v3/$INFURA_KEY\",\"linea-goerli\":\"https://linea-goerli.infura.io/v3/$INFURA_KEY\",\"scroll-goerli\":\"https://alpha-rpc.scroll.io/l2\",\"scroll\":\"https://rpc.scroll.io\"}')[inputs.network] }}" + ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"mainnet\":\"https://mainnet.infura.io/v3/$INFURA_KEY\",\"goerli\":\"https://goerli.infura.io/v3/$INFURA_KEY\",\"sepolia\":\"https://sepolia.infura.io/v3/$INFURA_KEY\",\"mumbai\":\"https://polygon-mumbai.infura.io/v3/$INFURA_KEY\",\"polygon\":\"https://polygon-mainnet.infura.io/v3/$INFURA_KEY\",\"arbitrum-goerli\":\"https://arbitrum-goerli.infura.io/v3/$INFURA_KEY\",\"arbitrum\":\"https://arbitrum-mainnet.infura.io/v3/$INFURA_KEY\",\"base\":\"https://clean-spring-wind.base-mainnet.discover.quiknode.pro/$QUICKNODE_KEY\",\"base-goerli\":\"https://base-goerli.infura.io/v3/$INFURA_KEY\",\"base-sepolia\":\"https://clean-spring-wind.base-sepolia.discover.quiknode.pro/$QUICKNODE_KEY\",\"linea-goerli\":\"https://linea-goerli.infura.io/v3/$INFURA_KEY\",\"scroll-goerli\":\"https://alpha-rpc.scroll.io/l2\",\"scroll\":\"https://rpc.scroll.io\"}')[inputs.network] }}" port: 8585 if: github.event.inputs.eth_pk == '' diff --git a/.github/workflows/enact-migration.yaml b/.github/workflows/enact-migration.yaml index 2ecc8c8ec..ea7993eb6 100644 --- a/.github/workflows/enact-migration.yaml +++ b/.github/workflows/enact-migration.yaml @@ -16,6 +16,7 @@ on: - arbitrum-goerli - base - base-goerli + - base-sepolia - linea-goerli - scroll-goerli - scroll @@ -56,21 +57,23 @@ jobs: echo "GOV_NETWORK=mainnet" >> $GITHUB_ENV ;; mumbai | arbitrum-goerli | base-goerli | linea-goerli | scroll-goerli | scroll) echo "GOV_NETWORK=goerli" >> $GITHUB_ENV ;; + base-sepolia) + echo "GOV_NETWORK=sepolia" >> $GITHUB_ENV ;; *) echo "No governance network for selected network" ;; esac - name: Seacrest - uses: hayesgm/seacrest@5748b3a066f517973ca2ca03d0af39bbf2b82d10 + uses: hayesgm/seacrest@1dc2d6e3750d5f5589173555220695a09b913071 with: wallet_connect_project_id: ${{ secrets.WALLET_CONNECT_PROJECT_ID }} requested_network: "${{ inputs.network }}" - ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"mainnet\":\"https://mainnet.infura.io/v3/$INFURA_KEY\",\"goerli\":\"https://goerli.infura.io/v3/$INFURA_KEY\",\"sepolia\":\"https://sepolia.infura.io/v3/$INFURA_KEY\",\"mumbai\":\"https://polygon-mumbai.infura.io/v3/$INFURA_KEY\",\"polygon\":\"https://polygon-mainnet.infura.io/v3/$INFURA_KEY\",\"arbitrum-goerli\":\"https://arbitrum-goerli.infura.io/v3/$INFURA_KEY\",\"arbitrum\":\"https://arbitrum-mainnet.infura.io/v3/$INFURA_KEY\",\"base\":\"https://clean-spring-wind.base-mainnet.discover.quiknode.pro/$QUICKNODE_KEY\",\"base-goerli\":\"https://base-goerli.infura.io/v3/$INFURA_KEY\",\"linea-goerli\":\"https://linea-goerli.infura.io/v3/$INFURA_KEY\",\"scroll-goerli\":\"https://alpha-rpc.scroll.io/l2\",\"scroll\":\"https://rpc.scroll.io\"}')[inputs.network] }}" + ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"mainnet\":\"https://mainnet.infura.io/v3/$INFURA_KEY\",\"goerli\":\"https://goerli.infura.io/v3/$INFURA_KEY\",\"sepolia\":\"https://sepolia.infura.io/v3/$INFURA_KEY\",\"mumbai\":\"https://polygon-mumbai.infura.io/v3/$INFURA_KEY\",\"polygon\":\"https://polygon-mainnet.infura.io/v3/$INFURA_KEY\",\"arbitrum-goerli\":\"https://arbitrum-goerli.infura.io/v3/$INFURA_KEY\",\"arbitrum\":\"https://arbitrum-mainnet.infura.io/v3/$INFURA_KEY\",\"base\":\"https://clean-spring-wind.base-mainnet.discover.quiknode.pro/$QUICKNODE_KEY\",\"base-goerli\":\"https://base-goerli.infura.io/v3/$INFURA_KEY\",\"linea-goerli\":\"https://linea-goerli.infura.io/v3/$INFURA_KEY\",\"scroll-goerli\":\"https://alpha-rpc.scroll.io/l2\",\"scroll\":\"https://rpc.scroll.io\",\"base-sepolia\":\"https://clean-spring-wind.base-sepolia.discover.quiknode.pro/$QUICKNODE_KEY\"}')[inputs.network] }}" port: 8585 if: github.event.inputs.eth_pk == '' - name: Seacrest (governance network) - uses: hayesgm/seacrest@5748b3a066f517973ca2ca03d0af39bbf2b82d10 + uses: hayesgm/seacrest@1dc2d6e3750d5f5589173555220695a09b913071 with: wallet_connect_project_id: ${{ secrets.WALLET_CONNECT_PROJECT_ID }} requested_network: "${{ env.GOV_NETWORK }}" diff --git a/.github/workflows/prepare-migration.yaml b/.github/workflows/prepare-migration.yaml index 3701da08d..0ea71b6b6 100644 --- a/.github/workflows/prepare-migration.yaml +++ b/.github/workflows/prepare-migration.yaml @@ -16,6 +16,7 @@ on: - arbitrum-goerli - base - base-goerli + - base-sepolia deployment: description: Deployment Name (e.g. "usdc") required: true @@ -42,11 +43,11 @@ jobs: LINEASCAN_KEY: ${{ secrets.LINEASCAN_KEY }} steps: - name: Seacrest - uses: hayesgm/seacrest@5748b3a066f517973ca2ca03d0af39bbf2b82d10 + uses: hayesgm/seacrest@1dc2d6e3750d5f5589173555220695a09b913071 with: wallet_connect_project_id: ${{ secrets.WALLET_CONNECT_PROJECT_ID }} requested_network: "${{ inputs.network }}" - ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"mainnet\":\"https://mainnet.infura.io/v3/$INFURA_KEY\",\"goerli\":\"https://goerli.infura.io/v3/$INFURA_KEY\",\"sepolia\":\"https://sepolia.infura.io/v3/$INFURA_KEY\",\"mumbai\":\"https://polygon-mumbai.infura.io/v3/$INFURA_KEY\",\"polygon\":\"https://polygon-mainnet.infura.io/v3/$INFURA_KEY\",\"arbitrum-goerli\":\"https://arbitrum-goerli.infura.io/v3/$INFURA_KEY\",\"arbitrum\":\"https://arbitrum-mainnet.infura.io/v3/$INFURA_KEY\",\"base\":\"https://clean-spring-wind.base-mainnet.discover.quiknode.pro/$QUICKNODE_KEY\",\"base-goerli\":\"https://base-goerli.infura.io/v3/$INFURA_KEY\",\"linea-goerli\":\"https://linea-goerli.infura.io/v3/$INFURA_KEY\",\"scroll-goerli\":\"https://alpha-rpc.scroll.io/l2\",\"scroll\":\"https://rpc.scroll.io\"}')[inputs.network] }}" + ethereum_url: "${{ fromJSON('{\"fuji\":\"https://api.avax-test.network/ext/bc/C/rpc\",\"mainnet\":\"https://mainnet.infura.io/v3/$INFURA_KEY\",\"goerli\":\"https://goerli.infura.io/v3/$INFURA_KEY\",\"sepolia\":\"https://sepolia.infura.io/v3/$INFURA_KEY\",\"mumbai\":\"https://polygon-mumbai.infura.io/v3/$INFURA_KEY\",\"polygon\":\"https://polygon-mainnet.infura.io/v3/$INFURA_KEY\",\"arbitrum-goerli\":\"https://arbitrum-goerli.infura.io/v3/$INFURA_KEY\",\"arbitrum\":\"https://arbitrum-mainnet.infura.io/v3/$INFURA_KEY\",\"base\":\"https://clean-spring-wind.base-mainnet.discover.quiknode.pro/$QUICKNODE_KEY\",\"base-goerli\":\"https://base-goerli.infura.io/v3/$INFURA_KEY\",\"base-sepolia\":\"https://clean-spring-wind.base-sepolia.discover.quiknode.pro/$QUICKNODE_KEY\",\"linea-goerli\":\"https://linea-goerli.infura.io/v3/$INFURA_KEY\",\"scroll-goerli\":\"https://alpha-rpc.scroll.io/l2\",\"scroll\":\"https://rpc.scroll.io\"}')[inputs.network] }}" port: 8585 if: github.event.inputs.eth_pk == '' diff --git a/.github/workflows/run-scenarios.yaml b/.github/workflows/run-scenarios.yaml index 8acd648f5..260244151 100644 --- a/.github/workflows/run-scenarios.yaml +++ b/.github/workflows/run-scenarios.yaml @@ -7,7 +7,7 @@ jobs: strategy: fail-fast: false matrix: - bases: [ development, mainnet, mainnet-weth, goerli, goerli-weth, sepolia-usdc, sepolia-weth, fuji, mumbai, polygon, arbitrum-usdc.e, arbitrum-usdc, arbitrum-goerli-usdc, arbitrum-goerli-usdc.e, base-usdbc, base-weth, base-usdc, base-goerli, base-goerli-weth, linea-goerli, scroll-goerli, scroll-usdc] + bases: [ development, mainnet, mainnet-weth, goerli, goerli-weth, sepolia-usdc, sepolia-weth, fuji, mumbai, polygon, arbitrum-usdc.e, arbitrum-usdc, arbitrum-goerli-usdc, arbitrum-goerli-usdc.e, base-usdbc, base-weth, base-usdc, base-goerli, base-goerli-weth, base-sepolia, linea-goerli, scroll-goerli, scroll-usdc] name: Run scenarios env: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_KEY }} diff --git a/contracts/bridges/optimism/IL1CrossDomainMessenger.sol b/contracts/bridges/optimism/IL1CrossDomainMessenger.sol new file mode 100644 index 000000000..2658efdff --- /dev/null +++ b/contracts/bridges/optimism/IL1CrossDomainMessenger.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.15; + +interface IL1CrossDomainMessenger { + event FailedRelayedMessage(bytes32 indexed msgHash); + event Initialized(uint8 version); + event RelayedMessage(bytes32 indexed msgHash); + event SentMessage( + address indexed target, + address sender, + bytes message, + uint256 messageNonce, + uint256 gasLimit + ); + event SentMessageExtension1(address indexed sender, uint256 value); + + function MESSAGE_VERSION() external view returns (uint16); + + function MIN_GAS_CALLDATA_OVERHEAD() external view returns (uint64); + + function MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR() + external + view + returns (uint64); + + function MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR() + external + view + returns (uint64); + + function OTHER_MESSENGER() external view returns (address); + + function PORTAL() external view returns (address); + + function RELAY_CALL_OVERHEAD() external view returns (uint64); + + function RELAY_CONSTANT_OVERHEAD() external view returns (uint64); + + function RELAY_GAS_CHECK_BUFFER() external view returns (uint64); + + function RELAY_RESERVED_GAS() external view returns (uint64); + + function baseGas(bytes memory _message, uint32 _minGasLimit) + external + pure + returns (uint64); + + function failedMessages(bytes32) external view returns (bool); + + function initialize() external; + + function messageNonce() external view returns (uint256); + + function relayMessage( + uint256 _nonce, + address _sender, + address _target, + uint256 _value, + uint256 _minGasLimit, + bytes memory _message + ) external payable; + + function sendMessage( + address _target, + bytes memory _message, + uint32 _minGasLimit + ) external payable; + + function successfulMessages(bytes32) external view returns (bool); + + function version() external view returns (string memory); + + function xDomainMessageSender() external view returns (address); +} diff --git a/contracts/bridges/optimism/IL1StandardBridge.sol b/contracts/bridges/optimism/IL1StandardBridge.sol new file mode 100644 index 000000000..72a98f1bd --- /dev/null +++ b/contracts/bridges/optimism/IL1StandardBridge.sol @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.15; + +interface IL1StandardBridge { + event ERC20BridgeFinalized( + address indexed localToken, + address indexed remoteToken, + address indexed from, + address to, + uint256 amount, + bytes extraData + ); + event ERC20BridgeInitiated( + address indexed localToken, + address indexed remoteToken, + address indexed from, + address to, + uint256 amount, + bytes extraData + ); + event ERC20DepositInitiated( + address indexed l1Token, + address indexed l2Token, + address indexed from, + address to, + uint256 amount, + bytes extraData + ); + event ERC20WithdrawalFinalized( + address indexed l1Token, + address indexed l2Token, + address indexed from, + address to, + uint256 amount, + bytes extraData + ); + event ETHBridgeFinalized( + address indexed from, + address indexed to, + uint256 amount, + bytes extraData + ); + event ETHBridgeInitiated( + address indexed from, + address indexed to, + uint256 amount, + bytes extraData + ); + event ETHDepositInitiated( + address indexed from, + address indexed to, + uint256 amount, + bytes extraData + ); + event ETHWithdrawalFinalized( + address indexed from, + address indexed to, + uint256 amount, + bytes extraData + ); + + function MESSENGER() external view returns (address); + + function OTHER_BRIDGE() external view returns (address); + + function bridgeERC20( + address _localToken, + address _remoteToken, + uint256 _amount, + uint32 _minGasLimit, + bytes memory _extraData + ) external; + + function bridgeERC20To( + address _localToken, + address _remoteToken, + address _to, + uint256 _amount, + uint32 _minGasLimit, + bytes memory _extraData + ) external; + + function bridgeETH(uint32 _minGasLimit, bytes memory _extraData) + external + payable; + + function bridgeETHTo( + address _to, + uint32 _minGasLimit, + bytes memory _extraData + ) external payable; + + function depositERC20( + address _l1Token, + address _l2Token, + uint256 _amount, + uint32 _minGasLimit, + bytes memory _extraData + ) external; + + function depositERC20To( + address _l1Token, + address _l2Token, + address _to, + uint256 _amount, + uint32 _minGasLimit, + bytes memory _extraData + ) external; + + function depositETH(uint32 _minGasLimit, bytes memory _extraData) + external + payable; + + function depositETHTo( + address _to, + uint32 _minGasLimit, + bytes memory _extraData + ) external payable; + + function deposits(address, address) external view returns (uint256); + + function finalizeBridgeERC20( + address _localToken, + address _remoteToken, + address _from, + address _to, + uint256 _amount, + bytes memory _extraData + ) external; + + function finalizeBridgeETH( + address _from, + address _to, + uint256 _amount, + bytes memory _extraData + ) external payable; + + function finalizeERC20Withdrawal( + address _l1Token, + address _l2Token, + address _from, + address _to, + uint256 _amount, + bytes memory _extraData + ) external; + + function finalizeETHWithdrawal( + address _from, + address _to, + uint256 _amount, + bytes memory _extraData + ) external payable; + + function l2TokenBridge() external view returns (address); + + function messenger() external view returns (address); + + function version() external view returns (string memory); + + receive() external payable; +} diff --git a/deployments/base-sepolia/usdc/configuration.json b/deployments/base-sepolia/usdc/configuration.json new file mode 100644 index 000000000..5dd99ce7c --- /dev/null +++ b/deployments/base-sepolia/usdc/configuration.json @@ -0,0 +1,48 @@ +{ + "name": "Compound USDC", + "symbol": "cUSDCv3", + "baseToken": "USDC", + "baseTokenAddress": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + "baseTokenPriceFeed": "", + "pauseGuardian": "0x6106DA3AcFdEB341808f4DC3D2483eC67c98E728", + "borrowMin": "1e0", + "storeFrontPriceFactor": 0.5, + "targetReserves": "5000000e6", + "rates": { + "supplyKink": 0.85, + "supplySlopeLow": 0.048, + "supplySlopeHigh": 1.6, + "supplyBase": 0, + "borrowKink": 0.85, + "borrowSlopeLow": 0.053, + "borrowSlopeHigh": 1.7, + "borrowBase": 0.015 + }, + "tracking": { + "indexScale": "1e15", + "baseSupplySpeed": "0.000011574074074074073e15", + "baseBorrowSpeed": "0.0011458333333333333e15", + "baseMinForRewards": "100e6" + }, + "rewardToken": "COMP", + "assets": { + "cbETH": { + "address": "0x774eD9EDB0C5202dF9A86183804b5D9E99dC6CA3", + "priceFeed": "", + "decimals": "18", + "borrowCF": 0.75, + "liquidateCF": 0.8, + "liquidationFactor": 0.93, + "supplyCap": "800e18" + }, + "WETH": { + "address": "0x4200000000000000000000000000000000000006", + "priceFeed": "", + "decimals": "18", + "borrowCF": 0.775, + "liquidateCF": 0.825, + "liquidationFactor": 0.95, + "supplyCap": "1000e18" + } + } +} \ No newline at end of file diff --git a/deployments/base-sepolia/usdc/deploy.ts b/deployments/base-sepolia/usdc/deploy.ts new file mode 100644 index 000000000..5c1b92552 --- /dev/null +++ b/deployments/base-sepolia/usdc/deploy.ts @@ -0,0 +1,146 @@ +import { Deployed, DeploymentManager } from '../../../plugins/deployment_manager'; +import { DeploySpec, deployComet, exp } from '../../../src/deploy'; + +const SECONDS_PER_DAY = 24 * 60 * 60; + +const SEPOLIA_TIMELOCK = '0x54a06047087927D9B0fb21c1cf0ebd792764dDB8'; + +export default async function deploy(deploymentManager: DeploymentManager, deploySpec: DeploySpec): Promise { + const deployed = await deployContracts(deploymentManager, deploySpec); + return deployed; +} + +async function deployContracts( + deploymentManager: DeploymentManager, + deploySpec: DeploySpec +): Promise { + const trace = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + + // Pull in existing assets + const WETH = await deploymentManager.existing( + 'WETH', + '0x4200000000000000000000000000000000000006', + 'base-sepolia' + ); + + const cbETH = await deploymentManager.existing( + 'cbETH', + '0x774eD9EDB0C5202dF9A86183804b5D9E99dC6CA3', + 'base-sepolia', + 'contracts/ERC20.sol:ERC20' + ); + + const USDC = await deploymentManager.existing( + 'USDC', + '0x036CbD53842c5426634e7929541eC2318f3dCF7e', + 'base-sepolia' + ); + + const COMP = await deploymentManager.existing( + 'COMP', + '0x2f535da74048c0874400f0371Fba20DF983A56e2', + 'base-sepolia', + 'contracts/ERC20.sol:ERC20' + ); + + const l2CrossDomainMessenger = await deploymentManager.existing( + 'l2CrossDomainMessenger', + ['0xC0d3c0d3c0D3c0D3C0d3C0D3C0D3c0d3c0d30007', '0x4200000000000000000000000000000000000007'], + 'base-sepolia' + ); + + const l2StandardBridge = await deploymentManager.existing( + 'l2StandardBridge', + ['0xC0d3c0d3c0D3c0d3C0D3c0D3C0d3C0D3C0D30010', '0x4200000000000000000000000000000000000010'], + 'base-sepolia' + ); + + // Deploy constant price feed for USDC + const usdcConstantPriceFeed = await deploymentManager.deploy( + 'USDC:priceFeed', + 'pricefeeds/ConstantPriceFeed.sol', + [ + 8, // decimals + exp(1, 8) // constantPrice + ] + ); + + // Deploy ETH / USD SimplePriceFeed + const ethToUSDPriceFeed = await deploymentManager.deploy( + 'WETH:priceFeed', + 'test/SimplePriceFeed.sol', + [ + exp(3477.28, 8), // Latest answer on mainnet at block 19463076 + 8 + ] + ); + + const cbethToUSDPriceFeed = await deploymentManager.deploy( + 'cbETH:priceFeed', + 'test/SimplePriceFeed.sol', + [ + exp(3477.28, 8), // Latest answer on mainnet at block 19463076 + 8 + ] + ); + + // Deploy OptimismBridgeReceiver + const bridgeReceiver = await deploymentManager.deploy( + 'bridgeReceiver', + 'bridges/optimism/OptimismBridgeReceiver.sol', + [l2CrossDomainMessenger.address] + ); + + // Deploy Local Timelock + const localTimelock = await deploymentManager.deploy( + 'timelock', + 'vendor/Timelock.sol', + [ + bridgeReceiver.address, // admin + 10 * 60, // delay + 14 * SECONDS_PER_DAY, // grace period + 10 * 60, // minimum delay + 30 * SECONDS_PER_DAY // maximum delay + ] + ); + + // Initialize OptimismBridgeReceiver + await deploymentManager.idempotent( + async () => !(await bridgeReceiver.initialized()), + async () => { + trace(`Initializing BridgeReceiver`); + await bridgeReceiver.initialize( + SEPOLIA_TIMELOCK, // govTimelock + localTimelock.address // localTimelock + ); + trace(`BridgeReceiver initialized`); + } + ); + + // Deploy Comet + const deployed = await deployComet(deploymentManager, deploySpec); + const { comet } = deployed; + + // Deploy Bulker + const bulker = await deploymentManager.deploy( + 'bulker', + 'bulkers/BaseBulker.sol', + [ + await comet.governor(), // admin + WETH.address // weth + ] + ); + + // Deploy fauceteer + const fauceteer = await deploymentManager.deploy('fauceteer', 'test/Fauceteer.sol', []); + + return { + ...deployed, + bridgeReceiver, + l2CrossDomainMessenger, + l2StandardBridge, + bulker, + fauceteer + }; +} diff --git a/deployments/base-sepolia/usdc/migrations/1710879470_configure_and_ens.ts b/deployments/base-sepolia/usdc/migrations/1710879470_configure_and_ens.ts new file mode 100644 index 000000000..286724c8b --- /dev/null +++ b/deployments/base-sepolia/usdc/migrations/1710879470_configure_and_ens.ts @@ -0,0 +1,229 @@ +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { diffState, getCometConfig } from '../../../../plugins/deployment_manager/DiffState'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { calldata, exp, getConfigurationStruct, proposal } from '../../../../src/deploy'; +import { expect } from 'chai'; + +const ENSName = 'compound-community-licenses.eth'; +const ENSResolverAddress = '0x8FADE66B79cC9f707aB26799354482EB93a5B7dD'; +const ENSRegistryAddress = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'; +const ENSSubdomainLabel = 'v3-additional-grants'; +const ENSSubdomain = `${ENSSubdomainLabel}.${ENSName}`; +const ENSTextRecordKey = 'v3-official-markets'; +const baseSepoliaCOMPAddress = '0x2f535da74048c0874400f0371Fba20DF983A56e2'; + +export default migration('1710879470_configure_and_ens', { + prepare: async (deploymentManager: DeploymentManager) => { + return {}; + }, + + enact: async (deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager) => { + const trace = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + const { utils } = ethers; + + const { + bridgeReceiver, + comet, + cometAdmin, + configurator, + rewards, + } = await deploymentManager.getContracts(); + + const { + baseL1CrossDomainMessenger, + baseL1StandardBridge, + governor, + COMP: sepoliaCOMP, + } = await govDeploymentManager.getContracts(); + + // // ENS Setup + // // See also: https://docs.ens.domains/contract-api-reference/name-processing + // const ENSResolver = await govDeploymentManager.existing('ENSResolver', ENSResolverAddress, 'sepolia'); + // const subdomainHash = ethers.utils.namehash(ENSSubdomain); + // const chainId = (await deploymentManager.hre.ethers.provider.getNetwork()).chainId.toString(); + // const newMarketObject = { baseSymbol: 'USDC', cometAddress: comet.address }; + // const officialMarketsJSON = JSON.parse(await ENSResolver.text(subdomainHash, ENSTextRecordKey)); + // if (officialMarketsJSON[chainId]) { + // officialMarketsJSON[chainId].push(newMarketObject); + // } else { + // officialMarketsJSON[chainId] = [newMarketObject]; + // } + + const configuration = await getConfigurationStruct(deploymentManager); + + const setConfigurationCalldata = await calldata( + configurator.populateTransaction.setConfiguration(comet.address, configuration) + ); + const deployAndUpgradeToCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [configurator.address, comet.address] + ); + const setRewardConfigCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [comet.address, baseSepoliaCOMPAddress] + ); + + // Note reawrd config was already set in deploy, so it's not needed here + const l2ProposalData = utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'string[]', 'bytes[]'], + [ + [ + configurator.address, + cometAdmin.address, + // rewards.address + ], + [ + 0, + 0, + // 0 + ], + [ + 'setConfiguration(address,(address,address,address,address,address,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint104,uint104,uint104,(address,address,uint8,uint64,uint64,uint64,uint128)[]))', + 'deployAndUpgradeTo(address,address)', + // 'setRewardConfig(address,address)' + ], + [ + setConfigurationCalldata, + deployAndUpgradeToCalldata, + // setRewardConfigCalldata + ] + ] + ); + + const COMPAmountToBridge = exp(10_000, 18); + + // Note: We aren't bridging USDC over to Base Sepolia because they don't use a bridged version of USDC there, + const sepoliaActions = [ + // 1. Set Comet configuration + deployAndUpgradeTo new Comet and set reward config on Base-Sepolia. + { + contract: baseL1CrossDomainMessenger, + signature: 'sendMessage(address,bytes,uint32)', + args: [bridgeReceiver.address, l2ProposalData, 2_500_000] + }, + + // 2. Approve Sepolia's L1StandardBridge to take Timelock's COMP (for bridging) + { + contract: sepoliaCOMP, + signature: 'approve(address,uint256)', + args: [baseL1StandardBridge.address, COMPAmountToBridge] + }, + + // 3. Bridge COMP from Sepolia to Base-Sepolia Comet using L1StandardBridge + { + contract: baseL1StandardBridge, + // function depositERC20To(address _l1Token, address _l2Token, address _to, uint256 _amount, uint32 _l2Gas,bytes calldata _data) + signature: 'depositERC20To(address,address,address,uint256,uint32,bytes)', + args: [sepoliaCOMP.address, baseSepoliaCOMPAddress, rewards.address, COMPAmountToBridge, 200_000, '0x'] + }, + + // Note, no ENS set up on Sepolia + // 4. Update the list of official markets + // { + // target: ENSResolverAddress, + // signature: 'setText(bytes32,string,string)', + // calldata: ethers.utils.defaultAbiCoder.encode( + // ['bytes32', 'string', 'string'], + // [subdomainHash, ENSTextRecordKey, JSON.stringify(officialMarketsJSON)] + // ) + // }, + ]; + + // const description = "# Configurate Base-Sepolia cUSDCv3 market, set reward config, bridge over USDC and COMP, and update ENS text record."; + const description = "# Configure Base-Sepolia cUSDCv3 market, set reward config, and bridge over COMP."; + const txn = await govDeploymentManager.retry(async () => + trace(await governor.propose(...(await proposal(sepoliaActions, description)))) + ); + + const event = txn.events.find(event => event.event === 'ProposalCreated'); + const [proposalId] = event.args; + + trace(`Created proposal ${proposalId}.`); + }, + + async enacted(deploymentManager: DeploymentManager): Promise { + return true; + }, + + async verify(deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager, preMigrationBlockNumber: number) { + const ethers = deploymentManager.hre.ethers; + await deploymentManager.spider(); // We spider here to pull in Optimism COMP now that reward config has been set + + const { + comet, + rewards, + COMP, + } = await deploymentManager.getContracts(); + + // 1. + const stateChanges = await diffState(comet, getCometConfig, preMigrationBlockNumber); + + //// Config was already set during deployment instead of subsequently with proposal. + // expect(stateChanges).to.deep.equal({ + // pauseGuardian: '0x6106DA3AcFdEB341808f4DC3D2483eC67c98E728', + // baseTrackingSupplySpeed: exp(34.74 / 86400, 15, 18), + // baseTrackingBorrowSpeed: exp(34.74 / 86400, 15, 18), + // baseBorrowMin: exp(1, 6), + // WETH: { + // supplyCap: exp(1000, 18) + // }, + // cbETH: { + // supplyCap: exp(800, 18) + // } + // }) + + expect(stateChanges).to.deep.equal({}); + + const config = await rewards.rewardConfig(comet.address); + expect(config.token).to.be.equal(COMP.address); + expect(config.rescaleFactor).to.be.equal(exp(1, 12)); + expect(config.shouldUpscale).to.be.equal(true); + + // 2. & 3. + expect(await COMP.balanceOf(rewards.address)).to.be.equal(exp(10_000, 18)); + + // ENS JSON string is not set up on Sepolia? + + // 4. + // const ENSResolver = await govDeploymentManager.existing('ENSResolver', ENSResolverAddress, 'sepolia'); + // const subdomainHash = ethers.utils.namehash(ENSSubdomain); + // const officialMarketsJSON = await ENSResolver.text(subdomainHash, ENSTextRecordKey); + // const officialMarkets = JSON.parse(officialMarketsJSON); + // expect(officialMarkets).to.deep.equal({ + // 5: [ + // { + // baseSymbol: 'USDC', + // cometAddress: '0x3EE77595A8459e93C2888b13aDB354017B198188', + // }, + // { + // baseSymbol: 'WETH', + // cometAddress: '0x9A539EEc489AAA03D588212a164d0abdB5F08F5F', + // }, + // ], + // 80001: [ + // { + // baseSymbol: 'USDC', + // cometAddress: '0xF09F0369aB0a875254fB565E52226c88f10Bc839', + // }, + // ], + // 420: [ + // { + // baseSymbol: 'USDC', + // cometAddress: '0xb8F2f9C84ceD7bBCcc1Db6FB7bb1F19A9a4adfF4' + // } + // ], + // 421613: [ + // { + // baseSymbol: 'USDC', + // cometAddress: '0x1d573274E19174260c5aCE3f2251598959d24456' + // } + // ], + // 84531: [ + // { + // baseSymbol: 'USDC', + // cometAddress: comet.address, + // }, + // ], + // }); + } +}); \ No newline at end of file diff --git a/deployments/base-sepolia/usdc/relations.ts b/deployments/base-sepolia/usdc/relations.ts new file mode 100644 index 000000000..f6010d1ea --- /dev/null +++ b/deployments/base-sepolia/usdc/relations.ts @@ -0,0 +1,34 @@ +import baseRelationConfig from '../../relations'; + +export default { + ...baseRelationConfig, + governor: { + artifact: 'contracts/bridges/optimism/OptimismBridgeReceiver.sol:OptimismBridgeReceiver' + }, + + // cbETH + '0x774ed9edb0c5202df9a86183804b5d9e99dc6ca3': { + artifact: 'contracts/ERC20.sol:ERC20', + }, + + // COMP + '0x2f535da74048c0874400f0371fba20df983a56e2': { + artifact: 'contracts/ERC20.sol:ERC20', + }, + + l2CrossDomainMessenger: { + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + + l2StandardBridge: { + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + } +}; \ No newline at end of file diff --git a/deployments/base-sepolia/usdc/roots.json b/deployments/base-sepolia/usdc/roots.json new file mode 100644 index 000000000..36c43dfac --- /dev/null +++ b/deployments/base-sepolia/usdc/roots.json @@ -0,0 +1,10 @@ +{ + "comet": "0x571621Ce60Cebb0c1D442B5afb38B1663C6Bf017", + "configurator": "0x090a2b1fc84d0b5141d5D5608b12Db19201aE5a6", + "rewards": "0x3394fa1baCC0b47dd0fF28C8573a476a161aF7BC", + "bridgeReceiver": "0x80c116493DB619560e3380D0fF195e749D8857D7", + "l2CrossDomainMessenger": "0x4200000000000000000000000000000000000007", + "l2StandardBridge": "0x4200000000000000000000000000000000000010", + "bulker": "0x7D25b2AecF07B5CB87B05e17Aa5cecbA8BCfDBD1", + "fauceteer": "0xD76cB57d8B097B80a6eE4D1b4d5ef872bfBa6051" +} \ No newline at end of file diff --git a/deployments/base-sepolia/weth/configuration.json b/deployments/base-sepolia/weth/configuration.json new file mode 100644 index 000000000..c337bba39 --- /dev/null +++ b/deployments/base-sepolia/weth/configuration.json @@ -0,0 +1,38 @@ +{ + "name": "Compound WETH", + "symbol": "cWETHv3", + "baseToken": "WETH", + "baseTokenAddress": "0x4200000000000000000000000000000000000006", + "baseTokenPriceFeed": "0x33376620e1A417100dE4e02104bD02C85e9F0627", + "pauseGuardian": "0x6106DA3AcFdEB341808f4DC3D2483eC67c98E728", + "borrowMin": "0.000001e18", + "storeFrontPriceFactor": 1, + "targetReserves": "5000e18", + "rates": { + "supplyKink": 0.9, + "supplySlopeLow": 0.0283824, + "supplySlopeHigh": 0.6066567706, + "supplyBase": 0, + "borrowKink": 0.9, + "borrowSlopeLow": 0.037, + "borrowSlopeHigh": 0.5171500339, + "borrowBase": 0.009945209674 + }, + "tracking": { + "indexScale": "1e15", + "baseSupplySpeed": "231_481_481_481e0", + "baseBorrowSpeed": "0e15", + "baseMinForRewards": "100e18" + }, + "rewardToken": "COMP", + "assets": { + "cbETH": { + "address": "0x774eD9EDB0C5202dF9A86183804b5D9E99dC6CA3", + "decimals": "18", + "borrowCF": 0.90, + "liquidateCF": 0.93, + "liquidationFactor": 0.975, + "supplyCap": "7500e18" + } + } +} \ No newline at end of file diff --git a/deployments/base-sepolia/weth/deploy.ts b/deployments/base-sepolia/weth/deploy.ts new file mode 100644 index 000000000..d86bd729c --- /dev/null +++ b/deployments/base-sepolia/weth/deploy.ts @@ -0,0 +1,77 @@ +import { Deployed, DeploymentManager } from '../../../plugins/deployment_manager'; +import { DeploySpec, deployComet, exp } from '../../../src/deploy'; + +export default async function deploy(deploymentManager: DeploymentManager, deploySpec: DeploySpec): Promise { + const deployed = await deployContracts(deploymentManager, deploySpec); + return deployed; +} + +async function deployContracts( + deploymentManager: DeploymentManager, + deploySpec: DeploySpec +): Promise { + const trace = deploymentManager.tracer(); + + // Deploy constant price feed for WETH + const wethConstantPriceFeed = await deploymentManager.deploy( + 'WETH:priceFeed', + 'pricefeeds/ConstantPriceFeed.sol', + [ + 8, // decimals + exp(1, 8) // constantPrice + ] + ); + + // Deploy scaling price feed for cbETH + // XXX There is no cbETH / ETH pricefeed on testnet. Remember to change this for mainnet. + // const cbETHScalingPriceFeed = await deploymentManager.deploy( + // 'cbETH:priceFeed', + // 'ScalingPriceFeed.sol', + // [ + // '0xcD2A119bD1F7DF95d706DE6F2057fDD45A0503E2', // cbETH / ETH price feed + // 8 // decimals + // ] + // ); + const cbETHConstantPriceFeed = await deploymentManager.deploy( + 'cbETH:priceFeed', + 'pricefeeds/ConstantPriceFeed.sol', + [ + 8, // decimals + exp(1, 8) // constantPrice + ] + ); + + const COMP = await deploymentManager.existing( + 'COMP', + '0x2f535da74048c0874400f0371Fba20DF983A56e2', + 'base-sepolia', + 'contracts/ERC20.sol:ERC20' + ); + + // Import shared contracts from cUSDCv3 + const cometAdmin = await deploymentManager.fromDep('cometAdmin', 'base-sepolia', 'usdc'); + const cometFactory = await deploymentManager.fromDep('cometFactory', 'base-sepolia', 'usdc'); + const $configuratorImpl = await deploymentManager.fromDep('configurator:implementation', 'base-sepolia', 'usdc'); + const configurator = await deploymentManager.fromDep('configurator', 'base-sepolia', 'usdc'); + const rewards = await deploymentManager.fromDep('rewards', 'base-sepolia', 'usdc'); + const bulker = await deploymentManager.fromDep('bulker', 'base-sepolia', 'usdc'); + const fauceteer = await deploymentManager.fromDep('fauceteer', 'base-sepolia', 'usdc'); + const l2CrossDomainMessenger = await deploymentManager.fromDep('l2CrossDomainMessenger', 'base-sepolia', 'usdc'); + const l2StandardBridge = await deploymentManager.fromDep('l2StandardBridge', 'base-sepolia', 'usdc'); + const localTimelock = await deploymentManager.fromDep('timelock', 'base-sepolia', 'usdc'); + const bridgeReceiver = await deploymentManager.fromDep('bridgeReceiver', 'base-sepolia', 'usdc'); + + // Deploy Comet + const deployed = await deployComet(deploymentManager, deploySpec); + + // XXX We will need to deploy a new bulker only if need to support wstETH + + return { + ...deployed, + bridgeReceiver, + l2CrossDomainMessenger, + l2StandardBridge, + bulker, + fauceteer + }; +} diff --git a/deployments/base-sepolia/weth/migrations/1711572958_initialize_market.ts b/deployments/base-sepolia/weth/migrations/1711572958_initialize_market.ts new file mode 100644 index 000000000..69351af89 --- /dev/null +++ b/deployments/base-sepolia/weth/migrations/1711572958_initialize_market.ts @@ -0,0 +1,115 @@ +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { diffState, getCometConfig } from '../../../../plugins/deployment_manager/DiffState'; +import { calldata, exp, getConfigurationStruct, proposal } from '../../../../src/deploy'; +import { expect } from 'chai'; + +const baseSepoliaCOMPAddress = '0x2f535da74048c0874400f0371Fba20DF983A56e2'; + +export default migration('1711572958_initialize_market', { + prepare: async (deploymentManager: DeploymentManager) => { + return {}; + }, + + enact: async (deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager, vars: Vars) => { + const trace = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + const { utils } = ethers; + + const cometFactory = await deploymentManager.fromDep('cometFactory', 'base-sepolia', 'usdc'); + const { + bridgeReceiver, + comet, + cometAdmin, + rewards, + configurator, + } = await deploymentManager.getContracts(); + + const { + baseL1CrossDomainMessenger, + governor, + } = await govDeploymentManager.getContracts(); + + const configuration = await getConfigurationStruct(deploymentManager); + const setFactoryCalldata = await calldata( + configurator.populateTransaction.setFactory(comet.address, cometFactory.address) + ); + const setConfigurationCalldata = await calldata( + configurator.populateTransaction.setConfiguration(comet.address, configuration) + ); + const deployAndUpgradeToCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [configurator.address, comet.address] + ); + const setRewardConfigCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [comet.address, baseSepoliaCOMPAddress] + ); + const l2ProposalData = utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'string[]', 'bytes[]'], + [ + [ configurator.address, configurator.address, cometAdmin.address, rewards.address ], + [ 0, 0, 0, 0 ], + [ + 'setFactory(address,address)', + 'setConfiguration(address,(address,address,address,address,address,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint104,uint104,uint104,(address,address,uint8,uint64,uint64,uint64,uint128)[]))', + 'deployAndUpgradeTo(address,address)', + 'setRewardConfig(address,address)', + ], + [ + setFactoryCalldata, + setConfigurationCalldata, + deployAndUpgradeToCalldata, + setRewardConfigCalldata + ] + ] + ); + + const sepoliaActions = [ + // 1. Set Comet configuration, deployAndUpgradeTo new Comet, set reward config + { + contract: baseL1CrossDomainMessenger, + signature: 'sendMessage(address,bytes,uint32)', + args: [ bridgeReceiver.address, l2ProposalData, 2_500_000 ] + }, + ]; + + const description = "# Configure Base-Sepolia cWETHv3 market, set rewards config."; + const txn = await govDeploymentManager.retry(async () => + trace(await governor.propose(...(await proposal(sepoliaActions, description)))) + ); + + const event = txn.events.find(event => event.event === 'ProposalCreated'); + const [proposalId] = event.args; + + trace(`Created proposal ${proposalId}.`); + }, + + async enacted(deploymentManager: DeploymentManager): Promise { + return true; + }, + + async verify(deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager, preMigrationBlockNumber: number) { + const ethers = deploymentManager.hre.ethers; + await deploymentManager.spider(); // We spider here to pull in Optimism COMP now that reward config has been set + + const { + comet, + rewards, + COMP, + } = await deploymentManager.getContracts(); + + // 1. + const stateChanges = await diffState(comet, getCometConfig, preMigrationBlockNumber); + + expect(stateChanges).to.deep.equal({ + pauseGuardian: '0x6106DA3AcFdEB341808f4DC3D2483eC67c98E728', + baseTrackingSupplySpeed: exp(2 / 86400, 16, 18), + // baseTrackingBorrowSpeed: exp(0, 16, 18), + baseBorrowMin: exp(0.000001, 18), + cbETH: { + supplyCap: exp(7500, 18) + }, + }); + } +}); \ No newline at end of file diff --git a/deployments/base-sepolia/weth/relations.ts b/deployments/base-sepolia/weth/relations.ts new file mode 100644 index 000000000..4792f02c6 --- /dev/null +++ b/deployments/base-sepolia/weth/relations.ts @@ -0,0 +1,39 @@ +import baseRelationConfig from '../../relations'; + +export default { + ...baseRelationConfig, + governor: { + artifact: 'contracts/bridges/optimism/OptimismBridgeReceiver.sol:OptimismBridgeReceiver' + }, + + // cbETH + '0x774ed9edb0c5202df9a86183804b5d9e99dc6ca3': { + artifact: 'contracts/ERC20.sol:ERC20', + }, + + // WETH + '0x4200000000000000000000000000000000000006': { + artifact: 'contracts/ERC20.sol:ERC20', + }, + + // COMP + '0x2f535da74048c0874400f0371fba20df983a56e2': { + artifact: 'contracts/ERC20.sol:ERC20', + }, + + l2CrossDomainMessenger: { + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + + l2StandardBridge: { + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + } +}; \ No newline at end of file diff --git a/deployments/base-sepolia/weth/roots.json b/deployments/base-sepolia/weth/roots.json new file mode 100644 index 000000000..8cec75273 --- /dev/null +++ b/deployments/base-sepolia/weth/roots.json @@ -0,0 +1,11 @@ +{ + "bridgeReceiver": "0x80c116493DB619560e3380D0fF195e749D8857D7", + "l2CrossDomainMessenger": "0x4200000000000000000000000000000000000007", + "l2StandardBridge": "0x4200000000000000000000000000000000000010", + "bulker": "0x7D25b2AecF07B5CB87B05e17Aa5cecbA8BCfDBD1", + "fauceteer": "0xD76cB57d8B097B80a6eE4D1b4d5ef872bfBa6051", + "COMP": "0x2f535da74048c0874400f0371Fba20DF983A56e2", + "comet": "0x61490650AbaA31393464C3f34E8B29cd1C44118E", + "configurator": "0x090a2b1fc84d0b5141d5D5608b12Db19201aE5a6", + "rewards": "0x3394fa1baCC0b47dd0fF28C8573a476a161aF7BC" +} \ No newline at end of file diff --git a/deployments/sepolia/usdc/relations.ts b/deployments/sepolia/usdc/relations.ts index f235b954a..0c817f86e 100644 --- a/deployments/sepolia/usdc/relations.ts +++ b/deployments/sepolia/usdc/relations.ts @@ -41,17 +41,14 @@ export default { } }, baseL1CrossDomainMessenger: { + artifact: 'contracts/bridges/optimism/IL1CrossDomainMessenger.sol:IL1CrossDomainMessenger', delegates: { // Not great, but this address shouldn't change and is very difficult to grab on-chain (private methods) - field: async () => '0xa042e16781484716c1Ef448c919af7BCd9607467' + field: async () => '0xC34855F4De64F1840e5686e64278da901e261f20' } }, baseL1StandardBridge: { - delegates: { - field: { - slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' - } - } + artifact: 'contracts/bridges/optimism/IL1StandardBridge.sol:IL1StandardBridge' }, lineaMessageService: { artifact: 'contracts/bridges/linea/IMessageService.sol:IMessageService', diff --git a/deployments/sepolia/usdc/roots.json b/deployments/sepolia/usdc/roots.json index 2df1bdf56..7df1916f1 100644 --- a/deployments/sepolia/usdc/roots.json +++ b/deployments/sepolia/usdc/roots.json @@ -3,5 +3,7 @@ "configurator": "0xc28aD44975C614EaBe0Ed090207314549e1c6624", "rewards": "0x8bF5b658bdF0388E8b482ED51B14aef58f90abfD", "fauceteer": "0x68793eA49297eB75DFB4610B68e076D2A5c7646C", - "bulker": "0x157c001bb1F8b33743B14483Be111C961d8e11dE" + "bulker": "0x157c001bb1F8b33743B14483Be111C961d8e11dE", + "baseL1CrossDomainMessenger": "0xC34855F4De64F1840e5686e64278da901e261f20", + "baseL1StandardBridge": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120" } \ No newline at end of file diff --git a/deployments/sepolia/weth/relations.ts b/deployments/sepolia/weth/relations.ts index 13f315c2a..ae12a73a5 100644 --- a/deployments/sepolia/weth/relations.ts +++ b/deployments/sepolia/weth/relations.ts @@ -10,4 +10,11 @@ export default { } } }, + baseL1CrossDomainMessenger: { + artifact: 'contracts/bridges/optimism/IL1CrossDomainMessenger.sol:IL1CrossDomainMessenger', + delegates: { + // Not great, but this address shouldn't change and is very difficult to grab on-chain (private methods) + field: async () => '0xC34855F4De64F1840e5686e64278da901e261f20' + } + }, }; diff --git a/deployments/sepolia/weth/roots.json b/deployments/sepolia/weth/roots.json index 825b5a431..851851976 100644 --- a/deployments/sepolia/weth/roots.json +++ b/deployments/sepolia/weth/roots.json @@ -1,7 +1,9 @@ { - "comet": "0x2943ac1216979aD8dB76D9147F64E61adc126e96", - "configurator": "0xc28aD44975C614EaBe0Ed090207314549e1c6624", - "rewards": "0x8bF5b658bdF0388E8b482ED51B14aef58f90abfD", - "bulker": "0xaD0C044425D81a2E223f4CE699156900fead2Aaa", - "fauceteer": "0x68793eA49297eB75DFB4610B68e076D2A5c7646C" + "comet": "0x2943ac1216979aD8dB76D9147F64E61adc126e96", + "configurator": "0xc28aD44975C614EaBe0Ed090207314549e1c6624", + "rewards": "0x8bF5b658bdF0388E8b482ED51B14aef58f90abfD", + "bulker": "0xaD0C044425D81a2E223f4CE699156900fead2Aaa", + "fauceteer": "0x68793eA49297eB75DFB4610B68e076D2A5c7646C", + "baseL1CrossDomainMessenger": "0xC34855F4De64F1840e5686e64278da901e261f20", + "baseL1StandardBridge": "0xfd0Bf71F60660E2f608ed56e1659C450eB113120" } \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 3b1799434..e328171d1 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -34,6 +34,8 @@ import baseUsdbcRelationConfigMap from './deployments/base/usdbc/relations'; import baseWethRelationConfigMap from './deployments/base/weth/relations'; import baseUsdcRelationConfigMap from './deployments/base/usdc/relations'; import baseGoerliRelationConfigMap from './deployments/base-goerli/usdc/relations'; +import baseSepoliaRelationConfigMap from './deployments/base-sepolia/usdc/relations'; +import baseSepoliaWethRelationConfigMap from './deployments/base-sepolia/weth/relations'; import baseGoerliWethRelationConfigMap from './deployments/base-goerli/weth/relations'; import lineaGoerliRelationConfigMap from './deployments/linea-goerli/usdc/relations'; import scrollGoerliRelationConfigMap from './deployments/scroll-goerli/usdc/relations'; @@ -142,6 +144,11 @@ const networkConfigs: NetworkConfig[] = [ chainId: 84531, url: `https://goerli.base.org/`, }, + { + network: 'base-sepolia', + chainId: 84532, + url: `https://clean-spring-wind.base-sepolia.discover.quiknode.pro/${QUICKNODE_KEY}`, + }, { network: 'linea-goerli', chainId: 59140, @@ -245,6 +252,7 @@ const config: HardhatUserConfig = { // Base base: BASESCAN_KEY, 'base-goerli': BASESCAN_KEY, + 'base-sepolia': BASESCAN_KEY, // Linea 'linea-goerli': LINEASCAN_KEY, // Scroll Testnet @@ -289,6 +297,14 @@ const config: HardhatUserConfig = { browserURL: 'https://goerli.basescan.org/' } }, + { + network: 'base-sepolia', + chainId: 84532, + urls: { + apiURL: 'https://api-sepolia.basescan.org/api', + browserURL: 'https://sepolia.basescan.org/' + } + }, { network: 'linea-goerli', chainId: 59140, @@ -359,6 +375,10 @@ const config: HardhatUserConfig = { usdc: baseGoerliRelationConfigMap, weth: baseGoerliWethRelationConfigMap }, + 'base-sepolia': { + usdc: baseSepoliaRelationConfigMap, + weth: baseSepoliaWethRelationConfigMap + }, 'linea-goerli': { usdc: lineaGoerliRelationConfigMap }, @@ -480,6 +500,18 @@ const config: HardhatUserConfig = { deployment: 'weth', auxiliaryBase: 'goerli' }, + { + name: 'base-sepolia-usdc', + network: 'base-sepolia', + deployment: 'usdc', + auxiliaryBase: 'sepolia-usdc' + }, + { + name: 'base-sepolia-weth', + network: 'base-sepolia', + deployment: 'weth', + auxiliaryBase: 'sepolia-weth' + }, { name: 'linea-goerli', network: 'linea-goerli', diff --git a/plugins/import/etherscan.ts b/plugins/import/etherscan.ts index 81b500b0a..1394951f1 100644 --- a/plugins/import/etherscan.ts +++ b/plugins/import/etherscan.ts @@ -21,6 +21,7 @@ export function getEtherscanApiUrl(network: string): string { 'arbitrum-goerli': 'api-goerli.arbiscan.io', base: 'api.basescan.org', 'base-goerli': 'api-goerli.basescan.org', + 'base-sepolia': 'api-sepolia.basescan.org', 'linea-goerli': 'api-goerli.lineascan.build', 'scroll-goerli': 'alpha-blockscout.scroll.io', scroll: 'api.scrollscan.com' @@ -48,6 +49,7 @@ export function getEtherscanUrl(network: string): string { 'arbitrum-goerli': 'goerli.arbiscan.io', base: 'basescan.org', 'base-goerli': 'goerli.basescan.org', + 'base-sepolia': 'sepolia.basescan.org', 'linea-goerli': 'goerli.lineascan.build', 'scroll-goerli': 'alpha-blockscout.scroll.io', scroll: 'scrollscan.com' @@ -75,6 +77,7 @@ export function getEtherscanApiKey(network: string): string { 'arbitrum-goerli': process.env.ARBISCAN_KEY, base: process.env.BASESCAN_KEY, 'base-goerli': process.env.BASESCAN_KEY, + 'base-sepolia': process.env.BASESCAN_KEY, 'linea-goerli': process.env.LINEASCAN_KEY, 'scroll-goerli': process.env.ETHERSCAN_KEY, scroll: process.env.ETHERSCAN_KEY diff --git a/scenario/utils/index.ts b/scenario/utils/index.ts index 061c5f7e7..2a88c4b84 100644 --- a/scenario/utils/index.ts +++ b/scenario/utils/index.ts @@ -476,6 +476,21 @@ export async function createCrossChainProposal(context: CometContext, l2Proposal calldata.push(sendMessageCalldata); break; } + case 'base-sepolia': { + const sendMessageCalldata = utils.defaultAbiCoder.encode( + ['address', 'bytes', 'uint32'], + [bridgeReceiver.address, l2ProposalData, 1_000_000] // XXX find a reliable way to estimate the gasLimit + ); + const baseL1CrossDomainMessenger = await govDeploymentManager.getContractOrThrow( + 'baseL1CrossDomainMessenger' + ); + + targets.push(baseL1CrossDomainMessenger.address); + values.push(0); + signatures.push('sendMessage(address,bytes,uint32)'); + calldata.push(sendMessageCalldata); + break; + } case 'mumbai': case 'polygon': { const sendMessageToChildCalldata = utils.defaultAbiCoder.encode( diff --git a/scenario/utils/isBridgeProposal.ts b/scenario/utils/isBridgeProposal.ts index 03ec57b26..6af74e1e0 100644 --- a/scenario/utils/isBridgeProposal.ts +++ b/scenario/utils/isBridgeProposal.ts @@ -44,6 +44,18 @@ export async function isBridgeProposal( const bridgeContracts = [baseL1CrossDomainMessenger.address, baseL1StandardBridge.address]; return targets.some(t => bridgeContracts.includes(t)); } + case 'base-sepolia': { + const governor = await governanceDeploymentManager.getContractOrThrow('governor'); + const baseL1CrossDomainMessenger = await governanceDeploymentManager.getContractOrThrow( + 'baseL1CrossDomainMessenger' + ); + const baseL1StandardBridge = await governanceDeploymentManager.getContractOrThrow( + 'baseL1StandardBridge' + ); + const { targets } = await governor.getActions(openProposal.id); + const bridgeContracts = [baseL1CrossDomainMessenger.address, baseL1StandardBridge.address]; + return targets.some(t => bridgeContracts.includes(t)); + } case 'linea-goerli': { const governor = await governanceDeploymentManager.getContractOrThrow('governor'); const lineaMessageService = await governanceDeploymentManager.getContractOrThrow( diff --git a/scenario/utils/relayMessage.ts b/scenario/utils/relayMessage.ts index 25ad63849..449322eb0 100644 --- a/scenario/utils/relayMessage.ts +++ b/scenario/utils/relayMessage.ts @@ -20,6 +20,13 @@ export default async function relayMessage( startingBlockNumber ); break; + case 'base-sepolia': + await relayBaseMessage( + governanceDeploymentManager, + bridgeDeploymentManager, + startingBlockNumber + ); + break; case 'mumbai': case 'polygon': await relayPolygonMessage( diff --git a/src/deploy/index.ts b/src/deploy/index.ts index be2d38860..a2dcfab4e 100644 --- a/src/deploy/index.ts +++ b/src/deploy/index.ts @@ -115,6 +115,9 @@ export const WHALES = { 'base-goerli': [ '0x21856935e5689490c72865f34CC665D0FF25664b' // USDC whale ], + 'base-sepolia': [ + '0xfaec9cdc3ef75713b48f46057b98ba04885e3391' // USDC whale + ], 'linea-goerli': [ '0xC858966280Da3Fa0348E51D2c3B892EcC889fC98', // USDC whale '0x44411c605eb7e009cad03f3847cfbbfcf8895130' // COMP whale diff --git a/tasks/deployment_manager/task.ts b/tasks/deployment_manager/task.ts index f68286910..09f49d56a 100644 --- a/tasks/deployment_manager/task.ts +++ b/tasks/deployment_manager/task.ts @@ -226,7 +226,7 @@ task('migrate', 'Runs migration') await runMigration(dm, governanceDm, prepare, enact, migration, overwrite); - if (enact && !noEnacted) { + if (enact && !noEnacted && !simulate) { await writeEnacted(migration, dm, true); } }