diff --git a/.github/workflows/run-scenarios.yaml b/.github/workflows/run-scenarios.yaml index fcff8271e..5da35d811 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, mainnet-usdt, mainnet-wsteth, goerli, goerli-weth, sepolia-usdc, sepolia-weth, fuji, mumbai, polygon, polygon-usdt, arbitrum-usdc.e, arbitrum-usdc, arbitrum-weth, arbitrum-usdt, arbitrum-goerli-usdc, arbitrum-goerli-usdc.e, base-usdbc, base-weth, base-usdc, base-goerli, base-goerli-weth, linea-goerli, optimism-usdc, optimism-usdt, optimism-weth, scroll-goerli, scroll-usdc] + bases: [ development, mainnet, mainnet-weth, mainnet-usdt, mainnet-wsteth, mainnet-usds, goerli, goerli-weth, sepolia-usdc, sepolia-weth, fuji, mumbai, polygon, polygon-usdt, arbitrum-usdc.e, arbitrum-usdc, arbitrum-weth, arbitrum-usdt, arbitrum-goerli-usdc, arbitrum-goerli-usdc.e, base-usdbc, base-weth, base-usdc, base-aero, base-goerli, base-goerli-weth, linea-goerli, optimism-usdc, optimism-usdt, optimism-weth, scroll-goerli, scroll-usdc] name: Run scenarios env: ETHERSCAN_KEY: ${{ secrets.ETHERSCAN_KEY }} diff --git a/deployments/base/aero/configuration.json b/deployments/base/aero/configuration.json new file mode 100644 index 000000000..1cf9ebd97 --- /dev/null +++ b/deployments/base/aero/configuration.json @@ -0,0 +1,60 @@ +{ + "name": "Compound AERO", + "symbol": "cAEROv3", + "baseToken": "AERO", + "baseTokenAddress": "0x940181a94A35A4569E4529A3CDfB74e38FD98631", + "borrowMin": "1e18", + "pauseGuardian": "0x3cb4653f3b45f448d9100b118b75a1503281d2ee", + "storeFrontPriceFactor": 0.6, + "targetReserves": "50000000e18", + "rates": { + "supplyKink": 0.85, + "supplySlopeLow": 0.08, + "supplySlopeHigh": 11, + "supplyBase": 0, + "borrowKink": 0.85, + "borrowSlopeLow": 0.0706, + "borrowSlopeHigh": 15, + "borrowBase": 0.04 + }, + "tracking": { + "indexScale": "1e15", + "baseSupplySpeed": "173611111111e0", + "baseBorrowSpeed": "173611111111e0", + "baseMinForRewards": "1000e18" + }, + "assets": { + "WETH": { + "address": "0x4200000000000000000000000000000000000006", + "decimals": "18", + "borrowCF": 0.6, + "liquidateCF": 0.65, + "liquidationFactor": 0.75, + "supplyCap": "7500e18" + }, + "USDC": { + "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "decimals": "6", + "borrowCF": 0.65, + "liquidateCF": 0.7, + "liquidationFactor": 0.8, + "supplyCap": "30000000e6" + }, + "wstETH": { + "address": "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", + "decimals": "18", + "borrowCF": 0.6, + "liquidateCF": 0.65, + "liquidationFactor": 0.75, + "supplyCap": "5000e18" + }, + "cbBTC": { + "address": "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", + "decimals": "8", + "borrowCF": 0.6, + "liquidateCF": 0.65, + "liquidationFactor": 0.75, + "supplyCap": "150e8" + } + } +} \ No newline at end of file diff --git a/deployments/base/aero/deploy.ts b/deployments/base/aero/deploy.ts new file mode 100644 index 000000000..0213e98ba --- /dev/null +++ b/deployments/base/aero/deploy.ts @@ -0,0 +1,119 @@ +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 aero = await deploymentManager.existing( + 'AERO', + '0x940181a94A35A4569E4529A3CDfB74e38FD98631', + 'base' + ); + const weth = await deploymentManager.existing( + 'WETH', + '0x4200000000000000000000000000000000000006', + 'base' + ); + const usdc = await deploymentManager.existing( + 'USDC', + '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', + 'base' + ); + const wstETH = await deploymentManager.existing( + 'wstETH', + '0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452', + 'base' + ); + const cbETH = await deploymentManager.existing( + 'cbETH', + '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf', + 'base' + ); + // AERO + // AERO -> USD + const aeroUsdPriceFeed = await deploymentManager.deploy( + 'AERO:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + '0x4EC5970fC728C5f65ba413992CD5fF6FD70fcfF0', + 8 + ] + ); + // USDC + // USDC -> USD + const usdcToUsdPriceFeed = await deploymentManager.deploy( + 'USDC:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + '0x7e860098F58bBFC8648a4311b374B1D669a2bc6B', // USDC -> USD + 8, + ], + true + ); + // WETH + // WETH -> USD + const ethToUsdPriceFeed = await deploymentManager.deploy( + 'WETH:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + '0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70', // ETH -> USD + 8, // decimals + ], + true + ); + // wstETH + // wstETH -> USD + const wstETHToUsdPriceFeed = await deploymentManager.deploy( + 'wstETH:priceFeed', + 'pricefeeds/MultiplicativePriceFeed.sol', + [ + '0xB88BAc61a4Ca37C43a3725912B1f472c9A5bc061', + '0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70', + 8, // decimals + 'wstETH / stETH ETH / USD' + ], + true + ); + // cbBTC + // cbBTC -> USD + const cbBTCToUsdPriceFeed = await deploymentManager.deploy( + 'cbBTC:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + '0x07DA0E54543a844a80ABE69c8A12F22B3aA59f9D', // cbBTC -> USD + 8, // decimals + ], + true + ); + + // Import shared contracts from cUSDbCv3 + const cometAdmin = await deploymentManager.fromDep('cometAdmin', 'base', 'usdbc'); + // new comet factory + const $configuratorImpl = await deploymentManager.fromDep('configurator:implementation', 'base', 'usdbc'); + const configurator = await deploymentManager.fromDep('configurator', 'base', 'usdbc'); + const rewards = await deploymentManager.fromDep('rewards', 'base', 'usdbc'); + const bulker = await deploymentManager.fromDep('bulker', 'base', 'usdbc'); + const l2CrossDomainMessenger = await deploymentManager.fromDep('l2CrossDomainMessenger', 'base', 'usdbc'); + const l2StandardBridge = await deploymentManager.fromDep('l2StandardBridge', 'base', 'usdbc'); + const localTimelock = await deploymentManager.fromDep('timelock', 'base', 'usdbc'); + const bridgeReceiver = await deploymentManager.fromDep('bridgeReceiver', 'base', 'usdbc'); + + // 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, // TODO: don't have to part of roots. can be pulled via relations + l2StandardBridge, + bulker + }; +} diff --git a/deployments/base/aero/migrations/1728096598_configurate_and_ens.ts b/deployments/base/aero/migrations/1728096598_configurate_and_ens.ts new file mode 100644 index 000000000..ba8a47678 --- /dev/null +++ b/deployments/base/aero/migrations/1728096598_configurate_and_ens.ts @@ -0,0 +1,266 @@ +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'; +import { ethers } from 'ethers'; + +const ENSName = 'compound-community-licenses.eth'; +const ENSResolverAddress = '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41'; +const ENSRegistryAddress = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'; +const ENSSubdomainLabel = 'v3-additional-grants'; +const ENSSubdomain = `${ENSSubdomainLabel}.${ENSName}`; +const ENSTextRecordKey = 'v3-official-markets'; +const baseCOMPAddress = '0x9e1028F5F1D5eDE59748FFceE5532509976840E0'; + + +export default migration('1728096598_configurate_and_ens', { + prepare: async (deploymentManager: DeploymentManager) => { + const cometFactory = await deploymentManager.deploy('cometFactory', 'CometFactory.sol', [], true); + return { newFactoryAddress: cometFactory.address }; + }, + + enact: async (deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager, { newFactoryAddress }) => { + const trace = deploymentManager.tracer(); + const { utils } = ethers; + + const { + bridgeReceiver, + comet, + cometAdmin, + configurator, + rewards, + } = await deploymentManager.getContracts(); + + const { + baseL1CrossDomainMessenger, + governor, + } = await govDeploymentManager.getContracts(); + + // ENS Setup + // See also: https://docs.ens.domains/contract-api-reference/name-processing + const ENSResolver = await govDeploymentManager.existing('ENSResolver', ENSResolverAddress); + const subdomainHash = ethers.utils.namehash(ENSSubdomain); + const baseChainId = 8453; + const newMarketObject = { baseSymbol: 'AERO', cometAddress: comet.address }; + const officialMarketsJSON = JSON.parse(await ENSResolver.text(subdomainHash, ENSTextRecordKey)); + if (officialMarketsJSON[baseChainId]) { + officialMarketsJSON[baseChainId].push(newMarketObject); + } else { + officialMarketsJSON[baseChainId] = [newMarketObject]; + } + + const configuration = await getConfigurationStruct(deploymentManager); + const setFactoryCalldata = await calldata( + configurator.populateTransaction.setFactory(comet.address, newFactoryAddress) + ); + 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, baseCOMPAddress] + ); + + 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 actions = [ + // 1. Set Comet configuration + deployAndUpgradeTo new Comet, set reward config on Base, wrap ETH to WETH and transfer to Comet as reserves. + { + contract: baseL1CrossDomainMessenger, + signature: 'sendMessage(address,bytes,uint32)', + args: [bridgeReceiver.address, l2ProposalData, 3_000_000] + }, + // 2. 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 = '# Initialize cAEROv3 on Base network\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes the deployment of Compound III to the Base network. This proposal takes the governance steps recommended and necessary to initialize a Compound III AERO market on Base; upon execution, cAEROv3 will be ready for use. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based off of the [recommendations from Gauntlet](https://www.comp.xyz/t/gauntlet-base-aero-comet-recommendations/5790).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/937), [deploy market GitHub action run](https://github.com/woof-software/comet/actions/runs/11259792818) and [forum discussion](https://www.comp.xyz/t/gauntlet-base-aero-comet-recommendations/5790).\n\n\n## Proposal Actions\n\nThe first proposal action sets the CometFactory for the new Comet instance in the existing Configurator.\n\nThe second action configures the Comet instance in the Configurator.\n\nThe third action deploys an instance of the newly configured factory and upgrades the Comet instance to use that implementation.\n\nThe fourth action configures the existing rewards contract for the newly deployed Comet instance.\n\nTODO: Seed reserves.\n\nThe sixth action updates the ENS TXT record `v3-official-markets` on `v3-additional-grants.compound-community-licenses.eth`, updating the official markets JSON to include the new Ethereum Mainnet cwstETHv3 market.'; + const txn = await govDeploymentManager.retry(async () => + trace(await governor.propose(...(await proposal(actions, description)))) + ); + + const event = txn.events.find(event => event.event === 'ProposalCreated'); + const [proposalId] = event.args; + + trace(`Created proposal ${proposalId}.`); + }, + + async enacted(): Promise { + return false; + }, + + async verify(deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager, preMigrationBlockNumber: number) { + + const { + comet, + rewards, + COMP, + } = await deploymentManager.getContracts(); + + + const stateChanges = await diffState(comet, getCometConfig, preMigrationBlockNumber); + + // uncomment on on-chain proposal PR + expect(stateChanges).to.deep.equal({ + USDC: { + supplyCap: exp(30000000, 6) + }, + WETH: { + supplyCap: exp(7500, 18) + }, + wstETH: { + supplyCap: exp(5000, 18) + }, + cbBTC: { + supplyCap: exp(150, 8) + }, + baseTrackingSupplySpeed: exp(15 / 86400, 15, 18), // 173611111111 + baseTrackingBorrowSpeed: exp(15 / 86400, 15, 18), // 173611111111 + }); + + 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); + + + // 5. + const ENSResolver = await govDeploymentManager.existing( + 'ENSResolver', + ENSResolverAddress + ); + const subdomainHash = ethers.utils.namehash(ENSSubdomain); + const officialMarketsJSON = await ENSResolver.text( + subdomainHash, + ENSTextRecordKey + ); + const officialMarkets = JSON.parse(officialMarketsJSON); + + expect(officialMarkets).to.deep.equal({ + 1: [ + { + baseSymbol: 'USDC', + cometAddress: '0xc3d688B66703497DAA19211EEdff47f25384cdc3', + }, + { + baseSymbol: 'WETH', + cometAddress: '0xA17581A9E3356d9A858b789D68B4d866e593aE94', + }, + { + baseSymbol: 'USDT', + cometAddress: '0x3Afdc9BCA9213A35503b077a6072F3D0d5AB0840' + }, + { + baseSymbol: 'wstETH', + cometAddress: '0x3D0bb1ccaB520A66e607822fC55BC921738fAFE3', + }, + ], + 10: [ + { + baseSymbol: 'USDC', + cometAddress: '0x2e44e174f7D53F0212823acC11C01A11d58c5bCB', + }, + { + baseSymbol: 'USDT', + cometAddress: '0x995E394b8B2437aC8Ce61Ee0bC610D617962B214', + }, + { + baseSymbol: 'WETH', + cometAddress: '0xE36A30D249f7761327fd973001A32010b521b6Fd' + } + ], + 137: [ + { + baseSymbol: 'USDC', + cometAddress: '0xF25212E676D1F7F89Cd72fFEe66158f541246445', + }, + { + baseSymbol: 'USDT', + cometAddress: '0xaeB318360f27748Acb200CE616E389A6C9409a07', + }, + ], + 8453: [ + { + baseSymbol: 'USDbC', + cometAddress: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf', + }, + { + baseSymbol: 'WETH', + cometAddress: '0x46e6b214b524310239732D51387075E0e70970bf', + }, + { + baseSymbol: 'USDC', + cometAddress: '0xb125E6687d4313864e53df431d5425969c15Eb2F', + }, + { + baseSymbol: 'AERO', + cometAddress: comet.address, + }, + ], + 42161: [ + { + baseSymbol: 'USDC.e', + cometAddress: '0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA', + }, + { + baseSymbol: 'USDC', + cometAddress: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf', + }, + { + baseSymbol: 'WETH', + cometAddress: '0x6f7D514bbD4aFf3BcD1140B7344b32f063dEe486', + }, + { + baseSymbol: 'USDT', + cometAddress: '0xd98Be00b5D27fc98112BdE293e487f8D4cA57d07', + }, + ], + 534352: [ + { + baseSymbol: 'USDC', + cometAddress: '0xB2f97c1Bd3bf02f5e74d13f02E3e26F93D77CE44', + }, + ], + }); + } +}); \ No newline at end of file diff --git a/deployments/base/aero/relations.ts b/deployments/base/aero/relations.ts new file mode 100644 index 000000000..ac825da26 --- /dev/null +++ b/deployments/base/aero/relations.ts @@ -0,0 +1,52 @@ +import baseRelationConfig from '../../relations'; + +export default { + ...baseRelationConfig, + governor: { + artifact: 'contracts/bridges/optimism/OptimismBridgeReceiver.sol:OptimismBridgeReceiver' + }, + + Proxy: { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + + l2CrossDomainMessenger: { + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + + l2StandardBridge: { + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + + OssifiableProxy: { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + + TransparentUpgradeableProxy: { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, + +}; \ No newline at end of file diff --git a/deployments/base/aero/roots.json b/deployments/base/aero/roots.json new file mode 100644 index 000000000..fc5de7d9f --- /dev/null +++ b/deployments/base/aero/roots.json @@ -0,0 +1,10 @@ +{ + "COMP": "0x9e1028F5F1D5eDE59748FFceE5532509976840E0", + "comet": "0x784efeB622244d2348d4F2522f8860B96fbEcE89", + "configurator": "0x45939657d1CA34A8FA39A924B71D28Fe8431e581", + "rewards": "0x123964802e6ABabBE1Bc9547D72Ef1B69B00A6b1", + "bridgeReceiver": "0x18281dfC4d00905DA1aaA6731414EABa843c468A", + "l2CrossDomainMessenger": "0x4200000000000000000000000000000000000007", + "l2StandardBridge": "0x4200000000000000000000000000000000000010", + "bulker": "0x78D0677032A35c63D142a48A2037048871212a8C" +} \ No newline at end of file diff --git a/deployments/base/usdc/migrations/1726478432_add_cbbtc_as_collateral.ts b/deployments/base/usdc/migrations/1726478432_add_cbbtc_as_collateral.ts new file mode 100644 index 000000000..f2f472155 --- /dev/null +++ b/deployments/base/usdc/migrations/1726478432_add_cbbtc_as_collateral.ts @@ -0,0 +1,148 @@ +import { expect } from 'chai'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { calldata, exp, proposal } from '../../../../src/deploy'; +import { utils } from 'ethers'; + +const CBBTC_ADDRESS = '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf'; +const CBBTC_USD_PRICE_FEED_ADDRESS = '0x07DA0E54543a844a80ABE69c8A12F22B3aA59f9D'; +let newPriceFeedAddress: string; + +export default migration('1726478432_add_cbbtc_as_collateral', { + async prepare(deploymentManager: DeploymentManager) { + const _cbBTCPriceFeed = await deploymentManager.deploy( + 'cbBTC:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + CBBTC_USD_PRICE_FEED_ADDRESS, // BTC / USD price feed + 8, // decimals + ] + ); + return { cbBTCPriceFeedAddress: _cbBTCPriceFeed.address }; + }, + + enact: async ( + deploymentManager: DeploymentManager, + govDeploymentManager: DeploymentManager, + { cbBTCPriceFeedAddress } + ) => { + const trace = deploymentManager.tracer(); + + const cbBTC = await deploymentManager.existing( + 'cbBTC', + CBBTC_ADDRESS, + 'base', + 'contracts/ERC20.sol:ERC20' + ); + const cbBTCPriceFeed = await deploymentManager.existing( + 'cbBTC:priceFeed', + cbBTCPriceFeedAddress, + 'base' + ); + + const { + bridgeReceiver, + comet, + cometAdmin, + configurator, + } = await deploymentManager.getContracts(); + + const { governor, baseL1CrossDomainMessenger } = await govDeploymentManager.getContracts(); + + const newAssetConfig = { + asset: cbBTC.address, + priceFeed: cbBTCPriceFeed.address, + decimals: await cbBTC.decimals(), + borrowCollateralFactor: exp(0.80, 18), + liquidateCollateralFactor: exp(0.85, 18), + liquidationFactor: exp(0.95, 18), + supplyCap: exp(45, 8), + }; + + newPriceFeedAddress = cbBTCPriceFeed.address; + + const addAssetCalldata = await calldata( + configurator.populateTransaction.addAsset(comet.address, newAssetConfig) + ); + const deployAndUpgradeToCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [configurator.address, comet.address] + ); + + const l2ProposalData = utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'string[]', 'bytes[]'], + [ + [configurator.address, cometAdmin.address], + [0, 0], + [ + 'addAsset(address,(address,address,uint8,uint64,uint64,uint64,uint128))', + 'deployAndUpgradeTo(address,address)', + ], + [addAssetCalldata, deployAndUpgradeToCalldata], + ] + ); + + const mainnetActions = [ + // Send the proposal to the L2 bridge + { + contract: baseL1CrossDomainMessenger, + signature: 'sendMessage(address,bytes,uint32)', + args: [bridgeReceiver.address, l2ProposalData, 3_000_000] + }, + ]; + + const description = '# Add cbBTC as collateral into cUSDCv3 on Base\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes to add cbBTC into cUSDCv3 on Base network. This proposal takes the governance steps recommended and necessary to update a Compound III USDC market on Base. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based off of the [recommendations from Gauntlet](https://www.comp.xyz/t/add-collateral-cbbtc-to-weth-market-on-base-and-mainnet/5689/2).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/925) and [forum discussion](https://www.comp.xyz/t/add-collateral-cbbtc-to-weth-market-on-base-and-mainnet/5689).\n\n\n## Proposal Actions\n\nThe first proposal action adds cbBTC to the USDC Comet on Base. This sends the encoded `addAsset` and `deployAndUpgradeTo` calls across the bridge to the governance receiver on Base.'; + const txn = await govDeploymentManager.retry(async () => + trace( + await governor.propose(...(await proposal(mainnetActions, 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) { + const { comet, configurator } = await deploymentManager.getContracts(); + + const cbBTCAssetIndex = Number(await comet.numAssets()) - 1; + + const cbBTCAssetConfig = { + asset: CBBTC_ADDRESS, + priceFeed: newPriceFeedAddress, + decimals: 8, + borrowCollateralFactor: exp(0.80, 18), + liquidateCollateralFactor: exp(0.85, 18), + liquidationFactor: exp(0.95, 18), + supplyCap: exp(45, 8), + }; + + // 1. Compare proposed asset config with Comet asset info + const cbBTCAssetInfo = await comet.getAssetInfoByAddress(CBBTC_ADDRESS); + expect(cbBTCAssetIndex).to.be.equal(cbBTCAssetInfo.offset); + expect(cbBTCAssetConfig.asset).to.be.equal(cbBTCAssetInfo.asset); + expect(cbBTCAssetConfig.priceFeed).to.be.equal(cbBTCAssetInfo.priceFeed); + expect(exp(1, cbBTCAssetConfig.decimals)).to.be.equal(cbBTCAssetInfo.scale); + expect(cbBTCAssetConfig.borrowCollateralFactor).to.be.equal(cbBTCAssetInfo.borrowCollateralFactor); + expect(cbBTCAssetConfig.liquidateCollateralFactor).to.be.equal(cbBTCAssetInfo.liquidateCollateralFactor); + expect(cbBTCAssetConfig.liquidationFactor).to.be.equal(cbBTCAssetInfo.liquidationFactor); + expect(cbBTCAssetConfig.supplyCap).to.be.equal(cbBTCAssetInfo.supplyCap); + + // 2. Compare proposed asset config with Configurator asset config + const configuratorCbBTCAssetConfig = (await configurator.getConfiguration(comet.address)).assetConfigs[cbBTCAssetIndex]; + expect(cbBTCAssetConfig.asset).to.be.equal(configuratorCbBTCAssetConfig.asset); + expect(cbBTCAssetConfig.priceFeed).to.be.equal(configuratorCbBTCAssetConfig.priceFeed); + expect(cbBTCAssetConfig.decimals).to.be.equal(configuratorCbBTCAssetConfig.decimals); + expect(cbBTCAssetConfig.borrowCollateralFactor).to.be.equal(configuratorCbBTCAssetConfig.borrowCollateralFactor); + expect(cbBTCAssetConfig.liquidateCollateralFactor).to.be.equal(configuratorCbBTCAssetConfig.liquidateCollateralFactor); + expect(cbBTCAssetConfig.liquidationFactor).to.be.equal(configuratorCbBTCAssetConfig.liquidationFactor); + expect(cbBTCAssetConfig.supplyCap).to.be.equal(configuratorCbBTCAssetConfig.supplyCap); + }, +}); diff --git a/deployments/base/weth/migrations/1726480777_add_cbbtc_as_collateral.ts b/deployments/base/weth/migrations/1726480777_add_cbbtc_as_collateral.ts new file mode 100644 index 000000000..d89512145 --- /dev/null +++ b/deployments/base/weth/migrations/1726480777_add_cbbtc_as_collateral.ts @@ -0,0 +1,152 @@ +import { expect } from 'chai'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { calldata, exp, proposal } from '../../../../src/deploy'; +import { utils } from 'ethers'; + +const CBBTC_ADDRESS = '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf'; +const CBBTC_USD_PRICE_FEED_ADDRESS = '0x07DA0E54543a844a80ABE69c8A12F22B3aA59f9D'; +const ETH_USD_PRICE_FEED_ADDRESS = '0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70'; +let newPriceFeedAddress: string; + +export default migration('1726480777_add_cbbtc_as_collateral', { + async prepare(deploymentManager: DeploymentManager) { + const _cbBTCPriceFeed = await deploymentManager.deploy( + 'cbBTC:priceFeed', + 'pricefeeds/ReverseMultiplicativePriceFeed.sol', + [ + CBBTC_USD_PRICE_FEED_ADDRESS, // cbBTC / USD price feed + ETH_USD_PRICE_FEED_ADDRESS, // USD / ETH price feed + 8, // decimals + 'cbBTC / ETH price feed', // description + ] + ); + + return { cbBTCPriceFeedAddress: _cbBTCPriceFeed.address }; + }, + + enact: async ( + deploymentManager: DeploymentManager, + govDeploymentManager: DeploymentManager, + { cbBTCPriceFeedAddress } + ) => { + const trace = deploymentManager.tracer(); + + const cbBTC = await deploymentManager.existing( + 'cbBTC', + CBBTC_ADDRESS, + 'base', + 'contracts/ERC20.sol:ERC20' + ); + const cbBTCPriceFeed = await deploymentManager.existing( + 'cbBTC:priceFeed', + cbBTCPriceFeedAddress, + 'base' + ); + + const { + bridgeReceiver, + comet, + cometAdmin, + configurator, + } = await deploymentManager.getContracts(); + + const { governor, baseL1CrossDomainMessenger } = await govDeploymentManager.getContracts(); + + const newAssetConfig = { + asset: cbBTC.address, + priceFeed: cbBTCPriceFeed.address, + decimals: await cbBTC.decimals(), + borrowCollateralFactor: exp(0.80, 18), + liquidateCollateralFactor: exp(0.85, 18), + liquidationFactor: exp(0.95, 18), + supplyCap: exp(45, 8), + }; + + newPriceFeedAddress = cbBTCPriceFeed.address; + + const addAssetCalldata = await calldata( + configurator.populateTransaction.addAsset(comet.address, newAssetConfig) + ); + const deployAndUpgradeToCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [configurator.address, comet.address] + ); + + const l2ProposalData = utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'string[]', 'bytes[]'], + [ + [configurator.address, cometAdmin.address], + [0, 0], + [ + 'addAsset(address,(address,address,uint8,uint64,uint64,uint64,uint128))', + 'deployAndUpgradeTo(address,address)', + ], + [addAssetCalldata, deployAndUpgradeToCalldata], + ] + ); + + const mainnetActions = [ + // Send the proposal to the L2 bridge + { + contract: baseL1CrossDomainMessenger, + signature: 'sendMessage(address,bytes,uint32)', + args: [bridgeReceiver.address, l2ProposalData, 3_000_000] + }, + ]; + + const description = '# Add cbBTC as collateral into cWETHv3 on Base\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes to add cbBTC into cWETHv3 on Base network. This proposal takes the governance steps recommended and necessary to update a Compound III WETH market on Base. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based off of the [recommendations from Gauntlet](https://www.comp.xyz/t/add-collateral-cbbtc-to-weth-market-on-base-and-mainnet/5689/2).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/926) and [forum discussion](https://www.comp.xyz/t/add-collateral-cbbtc-to-weth-market-on-base-and-mainnet/5689).\n\n\n## Proposal Actions\n\nThe first proposal action adds cbBTC to the WETH Comet on Base. This sends the encoded `addAsset` and `deployAndUpgradeTo` calls across the bridge to the governance receiver on Base.'; + const txn = await govDeploymentManager.retry(async () => + trace( + await governor.propose(...(await proposal(mainnetActions, 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) { + const { comet, configurator } = await deploymentManager.getContracts(); + + const cbBTCAssetIndex = Number(await comet.numAssets()) - 1; + + const cbBTCAssetConfig = { + asset: CBBTC_ADDRESS, + priceFeed: newPriceFeedAddress, + decimals: 8, + borrowCollateralFactor: exp(0.80, 18), + liquidateCollateralFactor: exp(0.85, 18), + liquidationFactor: exp(0.95, 18), + supplyCap: exp(45, 8), + }; + + // 1. Compare proposed asset config with Comet asset info + const cbBTCAssetInfo = await comet.getAssetInfoByAddress(CBBTC_ADDRESS); + expect(cbBTCAssetIndex).to.be.equal(cbBTCAssetInfo.offset); + expect(cbBTCAssetConfig.asset).to.be.equal(cbBTCAssetInfo.asset); + expect(cbBTCAssetConfig.priceFeed).to.be.equal(cbBTCAssetInfo.priceFeed); + expect(exp(1, cbBTCAssetConfig.decimals)).to.be.equal(cbBTCAssetInfo.scale); + expect(cbBTCAssetConfig.borrowCollateralFactor).to.be.equal(cbBTCAssetInfo.borrowCollateralFactor); + expect(cbBTCAssetConfig.liquidateCollateralFactor).to.be.equal(cbBTCAssetInfo.liquidateCollateralFactor); + expect(cbBTCAssetConfig.liquidationFactor).to.be.equal(cbBTCAssetInfo.liquidationFactor); + expect(cbBTCAssetConfig.supplyCap).to.be.equal(cbBTCAssetInfo.supplyCap); + + // 2. Compare proposed asset config with Configurator asset config + const configuratorCbBTCAssetConfig = (await configurator.getConfiguration(comet.address)).assetConfigs[cbBTCAssetIndex]; + expect(cbBTCAssetConfig.asset).to.be.equal(configuratorCbBTCAssetConfig.asset); + expect(cbBTCAssetConfig.priceFeed).to.be.equal(configuratorCbBTCAssetConfig.priceFeed); + expect(cbBTCAssetConfig.decimals).to.be.equal(configuratorCbBTCAssetConfig.decimals); + expect(cbBTCAssetConfig.borrowCollateralFactor).to.be.equal(configuratorCbBTCAssetConfig.borrowCollateralFactor); + expect(cbBTCAssetConfig.liquidateCollateralFactor).to.be.equal(configuratorCbBTCAssetConfig.liquidateCollateralFactor); + expect(cbBTCAssetConfig.liquidationFactor).to.be.equal(configuratorCbBTCAssetConfig.liquidationFactor); + expect(cbBTCAssetConfig.supplyCap).to.be.equal(configuratorCbBTCAssetConfig.supplyCap); + }, +}); diff --git a/deployments/mainnet/usdc/migrations/1726215532_add_cbbtc_as_collateral.ts b/deployments/mainnet/usdc/migrations/1726215532_add_cbbtc_as_collateral.ts new file mode 100644 index 000000000..0c11923fa --- /dev/null +++ b/deployments/mainnet/usdc/migrations/1726215532_add_cbbtc_as_collateral.ts @@ -0,0 +1,125 @@ +import { expect } from 'chai'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { exp, proposal } from '../../../../src/deploy'; + +const CBBTC_ADDRESS = '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf'; +const CBBTC_USD_PRICE_FEED = '0x2665701293fCbEB223D11A08D826563EDcCE423A'; + +let priceFeedAddress: string; + +export default migration('1726215532_add_cbbtc_as_collateral', { + async prepare(deploymentManager: DeploymentManager) { + const _cbBTCPriceFeed = await deploymentManager.deploy( + 'cbBTC:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + CBBTC_USD_PRICE_FEED, // cbBTC / USD price feed + 8 // decimals + ] + ); + return { cbBTCPriceFeedAddress: _cbBTCPriceFeed.address }; + }, + + enact: async (deploymentManager: DeploymentManager, _, { cbBTCPriceFeedAddress }) => { + const trace = deploymentManager.tracer(); + + const cbBTC = await deploymentManager.existing( + 'cbBTC', + CBBTC_ADDRESS, + 'mainnet', + 'contracts/ERC20.sol:ERC20' + ); + const cbBTCPriceFeed = await deploymentManager.existing( + 'cbBTC:priceFeed', + cbBTCPriceFeedAddress, + 'mainnet' + ); + priceFeedAddress = cbBTCPriceFeed.address; + const { + governor, + comet, + cometAdmin, + configurator + } = await deploymentManager.getContracts(); + + const newAssetConfig = { + asset: cbBTC.address, + priceFeed: cbBTCPriceFeed.address, + decimals: await cbBTC.decimals(), + borrowCollateralFactor: exp(0.8, 18), + liquidateCollateralFactor: exp(0.85, 18), + liquidationFactor: exp(0.95, 18), + supplyCap: exp(93, 8), + }; + + const mainnetActions = [ + // 1. Add cbBTC as asset + { + contract: configurator, + signature: 'addAsset(address,(address,address,uint8,uint64,uint64,uint64,uint128))', + args: [comet.address, newAssetConfig], + }, + // 2. Deploy and upgrade to a new version of Comet + { + contract: cometAdmin, + signature: 'deployAndUpgradeTo(address,address)', + args: [configurator.address, comet.address], + }, + ]; + + const description = '# Add cbBTC as collateral into cUSDCv3 on Ethereum\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes to add cbBTC into cUSDCv3 on Ethereum network. This proposal takes the governance steps recommended and necessary to update a Compound III USDC market on Ethereum. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based off of the [recommendations from Gauntlet](https://www.comp.xyz/t/add-collateral-cbbtc-to-weth-market-on-base-and-mainnet/5689/2).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/921) and [forum discussion](https://www.comp.xyz/t/add-collateral-cbbtc-to-weth-market-on-base-and-mainnet/5689).\n\n\n## Proposal Actions\n\nThe first proposal action adds cbBTC asset as collateral with corresponding configurations.\n\nThe second action deploys and upgrades Comet to a new version.'; + const txn = await deploymentManager.retry(async () => + trace( + await governor.propose(...(await proposal(mainnetActions, 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) { + const { comet, configurator } = await deploymentManager.getContracts(); + + const cbBTCAssetIndex = Number(await comet.numAssets()) - 1; + + const cbBTCAssetConfig = { + asset: CBBTC_ADDRESS, + priceFeed: priceFeedAddress, + decimals: 8, + borrowCollateralFactor: exp(0.8, 18), + liquidateCollateralFactor: exp(0.85, 18), + liquidationFactor: exp(0.95, 18), + supplyCap: exp(93, 8), + }; + + // 1. Compare proposed asset config with Comet asset info + const cbBTCAssetInfo = await comet.getAssetInfoByAddress(CBBTC_ADDRESS); + expect(cbBTCAssetIndex).to.be.equal(cbBTCAssetInfo.offset); + expect(cbBTCAssetConfig.asset).to.be.equal(cbBTCAssetInfo.asset); + expect(cbBTCAssetConfig.priceFeed).to.be.equal(cbBTCAssetInfo.priceFeed); + expect(exp(1, cbBTCAssetConfig.decimals)).to.be.equal(cbBTCAssetInfo.scale); + expect(cbBTCAssetConfig.borrowCollateralFactor).to.be.equal(cbBTCAssetInfo.borrowCollateralFactor); + expect(cbBTCAssetConfig.liquidateCollateralFactor).to.be.equal(cbBTCAssetInfo.liquidateCollateralFactor); + expect(cbBTCAssetConfig.liquidationFactor).to.be.equal(cbBTCAssetInfo.liquidationFactor); + expect(cbBTCAssetConfig.supplyCap).to.be.equal(cbBTCAssetInfo.supplyCap); + + // 2. Compare proposed asset config with Configurator asset config + const configuratorcbBTCAssetConfig = (await configurator.getConfiguration(comet.address)).assetConfigs[cbBTCAssetIndex]; + expect(cbBTCAssetConfig.asset).to.be.equal(configuratorcbBTCAssetConfig.asset); + expect(cbBTCAssetConfig.priceFeed).to.be.equal(configuratorcbBTCAssetConfig.priceFeed); + expect(cbBTCAssetConfig.decimals).to.be.equal(configuratorcbBTCAssetConfig.decimals); + expect(cbBTCAssetConfig.borrowCollateralFactor).to.be.equal(configuratorcbBTCAssetConfig.borrowCollateralFactor); + expect(cbBTCAssetConfig.liquidateCollateralFactor).to.be.equal(configuratorcbBTCAssetConfig.liquidateCollateralFactor); + expect(cbBTCAssetConfig.liquidationFactor).to.be.equal(configuratorcbBTCAssetConfig.liquidationFactor); + expect(cbBTCAssetConfig.supplyCap).to.be.equal(configuratorcbBTCAssetConfig.supplyCap); + }, +}); diff --git a/deployments/mainnet/usdc/migrations/1728053491_add_tbtc_collateral.ts b/deployments/mainnet/usdc/migrations/1728053491_add_tbtc_collateral.ts new file mode 100644 index 000000000..9f27b3887 --- /dev/null +++ b/deployments/mainnet/usdc/migrations/1728053491_add_tbtc_collateral.ts @@ -0,0 +1,133 @@ +import { expect } from 'chai'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { exp, proposal } from '../../../../src/deploy'; + +const TBTC_ADDRESS = '0x18084fbA666a33d37592fA2633fD49a74DD93a88'; +const TBTC_TO_USD_PRICE_FEED = '0x8350b7De6a6a2C1368E7D4Bd968190e13E354297'; + +let newPriceFeedAddress: string; + +export default migration('1728053491_add_tbtc_collateral', { + async prepare(deploymentManager: DeploymentManager) { + const tBTCMultiplicativePriceFeed = await deploymentManager.deploy( + 'tBTC:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + TBTC_TO_USD_PRICE_FEED, // tBTC / USD price feed + 8, // decimals + ] + ); + return { tBTCPriceFeedAddress: tBTCMultiplicativePriceFeed.address }; + }, + + async enact(deploymentManager: DeploymentManager, _, { tBTCPriceFeedAddress }) { + + const trace = deploymentManager.tracer(); + + const tBTC = await deploymentManager.existing( + 'tBTC', + TBTC_ADDRESS, + 'mainnet', + 'contracts/ERC20.sol:ERC20' + ); + const tBTCPriceFeed = await deploymentManager.existing( + 'tBTC:priceFeed', + tBTCPriceFeedAddress, + 'mainnet' + ); + + newPriceFeedAddress = tBTCPriceFeedAddress; + + const { + governor, + comet, + cometAdmin, + configurator, + } = await deploymentManager.getContracts(); + + const tBTCAssetConfig = { + asset: tBTC.address, + priceFeed: tBTCPriceFeed.address, + decimals: await tBTC.decimals(), + borrowCollateralFactor: exp(0.76, 18), + liquidateCollateralFactor: exp(0.81, 18), + liquidationFactor: exp(0.9, 18), + supplyCap: exp(285, 18), + }; + + const mainnetActions = [ + // 1. Add tBTC as asset + { + contract: configurator, + signature: 'addAsset(address,(address,address,uint8,uint64,uint64,uint64,uint128))', + args: [comet.address, tBTCAssetConfig], + }, + // 2. Deploy and upgrade to a new version of Comet + { + contract: cometAdmin, + signature: 'deployAndUpgradeTo(address,address)', + args: [configurator.address, comet.address], + }, + ]; + + const description = '# Add tBTC as collateral into cUSDCv3 on Mainnet\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes to add tBTC into cUSDCv3 on Ethereum network. This proposal takes the governance steps recommended and necessary to update a Compound III USDC market on Ethereum. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based on the [recommendations from Gauntlet](https://www.comp.xyz/t/add-collateral-tbtc-to-eth-market-on-mainnet/5399/12).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/935) and [forum discussion](https://www.comp.xyz/t/add-collateral-tbtc-to-eth-market-on-mainnet/5399).\n\n\n## Proposal Actions\n\nThe first action adds tBTC asset as collateral with corresponding configurations.\n\nThe second action deploys and upgrades Comet to a new version.'; + const txn = await deploymentManager.retry(async () => + trace( + await governor.propose(...(await proposal(mainnetActions, 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) { + const { comet, configurator } = await deploymentManager.getContracts(); + + const tBTCAssetIndex = Number(await comet.numAssets()) - 1; + + const tBTC = await deploymentManager.existing( + 'tBTC', + TBTC_ADDRESS, + 'mainnet', + 'contracts/ERC20.sol:ERC20' + ); + const tBTCAssetConfig = { + asset: tBTC.address, + priceFeed: newPriceFeedAddress, + decimals: await tBTC.decimals(), + borrowCollateralFactor: exp(0.76, 18), + liquidateCollateralFactor: exp(0.81, 18), + liquidationFactor: exp(0.9, 18), + supplyCap: exp(285, 18), + }; + + // 1. Compare tBTC asset config with Comet and Configurator asset info + const cometTBTCAssetInfo = await comet.getAssetInfoByAddress(TBTC_ADDRESS); + expect(tBTCAssetIndex).to.be.equal(cometTBTCAssetInfo.offset); + expect(tBTCAssetConfig.asset).to.be.equal(cometTBTCAssetInfo.asset); + expect(tBTCAssetConfig.priceFeed).to.be.equal(cometTBTCAssetInfo.priceFeed); + expect(exp(1, tBTCAssetConfig.decimals)).to.be.equal(cometTBTCAssetInfo.scale); + expect(tBTCAssetConfig.borrowCollateralFactor).to.be.equal(cometTBTCAssetInfo.borrowCollateralFactor); + expect(tBTCAssetConfig.liquidateCollateralFactor).to.be.equal(cometTBTCAssetInfo.liquidateCollateralFactor); + expect(tBTCAssetConfig.liquidationFactor).to.be.equal(cometTBTCAssetInfo.liquidationFactor); + expect(tBTCAssetConfig.supplyCap).to.be.equal(cometTBTCAssetInfo.supplyCap); + + const configuratorTBTCAssetConfig = (await configurator.getConfiguration(comet.address)).assetConfigs[tBTCAssetIndex]; + expect(tBTCAssetConfig.asset).to.be.equal(configuratorTBTCAssetConfig.asset); + expect(tBTCAssetConfig.priceFeed).to.be.equal(configuratorTBTCAssetConfig.priceFeed); + expect(tBTCAssetConfig.decimals).to.be.equal(configuratorTBTCAssetConfig.decimals); + expect(tBTCAssetConfig.borrowCollateralFactor).to.be.equal(configuratorTBTCAssetConfig.borrowCollateralFactor); + expect(tBTCAssetConfig.liquidateCollateralFactor).to.be.equal(configuratorTBTCAssetConfig.liquidateCollateralFactor); + expect(tBTCAssetConfig.liquidationFactor).to.be.equal(configuratorTBTCAssetConfig.liquidationFactor); + expect(tBTCAssetConfig.supplyCap).to.be.equal(configuratorTBTCAssetConfig.supplyCap); + }, +}); \ No newline at end of file diff --git a/deployments/mainnet/usds/configuration.json b/deployments/mainnet/usds/configuration.json new file mode 100644 index 000000000..08c9c2de6 --- /dev/null +++ b/deployments/mainnet/usds/configuration.json @@ -0,0 +1,75 @@ +{ + "name": "Compound USDS", + "symbol": "cUSDSv3", + "baseToken": "USDS", + "baseTokenAddress": "0xdC035D45d973E3EC169d2276DDab16f1e407384F", + "borrowMin": "10e18", + "governor": "0x6d903f6003cca6255d85cca4d3b5e5146dc33925", + "pauseGuardian": "0xbbf3f1421d886e9b2c5d716b5192ac998af2012c", + "baseTokenPriceFeed": "0xfF30586cD0F29eD462364C7e81375FC0C71219b1", + "storeFrontPriceFactor": 0.6, + "targetReserves": "20_000_000e18", + "rates": { + "borrowBase": 0.015, + "borrowSlopeLow": 0.0333, + "borrowKink": 0.9, + "borrowSlopeHigh": 4.0, + "supplyBase": 0, + "supplySlopeLow": 0.039, + "supplyKink": 0.9, + "supplySlopeHigh": 3.6 + }, + "tracking": { + "indexScale": "1e15", + "baseSupplySpeed": "289351851851e0", + "baseBorrowSpeed": "289351851851e0", + "baseMinForRewards": "10000e18" + }, + "rewardTokenAddress": "0xc00e94Cb662C3520282E6f5717214004A7f26888", + "assets": { + "WETH": { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "priceFeed": "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419", + "decimals": "18", + "borrowCF": 0.83, + "liquidateCF": 0.9, + "liquidationFactor": 0.95, + "supplyCap": "50_000e18" + }, + "USDe": { + "address": "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3", + "priceFeed": "0xa569d910839Ae8865Da8F8e70FfFb0cBA869F961", + "decimals": "18", + "borrowCF": 0.7, + "liquidateCF": 0.75, + "liquidationFactor": 0.85, + "supplyCap": "50_000_000e18" + }, + "cbBTC": { + "address": "0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf", + "priceFeed": "0x2665701293fCbEB223D11A08D826563EDcCE423A", + "decimals": "8", + "borrowCF": 0.8, + "liquidateCF": 0.85, + "liquidationFactor": 0.95, + "supplyCap": "150e8" + }, + "tBTC": { + "address": "0x18084fbA666a33d37592fA2633fD49a74DD93a88", + "priceFeed": "0x8350b7De6a6a2C1368E7D4Bd968190e13E354297", + "decimals": "18", + "borrowCF": 0.76, + "liquidateCF": 0.81, + "liquidationFactor": 0.9, + "supplyCap": "285e18" + }, + "wstETH": { + "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + "decimals": "18", + "borrowCF": 0.82, + "liquidateCF": 0.87, + "liquidationFactor": 0.92, + "supplyCap": "10_000e18" + } + } +} \ No newline at end of file diff --git a/deployments/mainnet/usds/deploy.ts b/deployments/mainnet/usds/deploy.ts new file mode 100644 index 000000000..82bbcdbc0 --- /dev/null +++ b/deployments/mainnet/usds/deploy.ts @@ -0,0 +1,40 @@ +import { Deployed, DeploymentManager } from '../../../plugins/deployment_manager'; +import { DeploySpec, deployComet } from '../../../src/deploy'; + +export default async function deploy(deploymentManager: DeploymentManager, deploySpec: DeploySpec): Promise { + const WETH = await deploymentManager.existing('WETH', '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'); + const COMP = await deploymentManager.existing('COMP', '0xc00e94Cb662C3520282E6f5717214004A7f26888'); + const wstETH = await deploymentManager.existing('wstETH', '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0'); + const cbBTC = await deploymentManager.existing('cbBTC', '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf'); + const tBTC = await deploymentManager.existing('tBTC', '0x18084fbA666a33d37592fA2633fD49a74DD93a88'); + const USDe = await deploymentManager.existing('USDe', '0x4c9EDD5852cd905f086C759E8383e09bff1E68B3'); + const USDS = await deploymentManager.existing('USDS', '0xdC035D45d973E3EC169d2276DDab16f1e407384F'); + + const wstETHtoUsdPriceFeed = await deploymentManager.deploy( + 'wstETH:priceFeed', + 'pricefeeds/WstETHPriceFeed.sol', + [ + '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419', // ETH / USD price feed + wstETH.address, // wstETH token + 8, // decimals + ], + true + ); + + // Import shared contracts from cUSDCv3 + const cometAdmin = await deploymentManager.fromDep('cometAdmin', 'mainnet', 'usdc'); + const $configuratorImpl = await deploymentManager.fromDep('configurator:implementation', 'mainnet', 'usdc'); + const configurator = await deploymentManager.fromDep('configurator', 'mainnet', 'usdc'); + const rewards = await deploymentManager.fromDep('rewards', 'mainnet', 'usdc'); + const bulker = await deploymentManager.fromDep('bulker', 'mainnet', 'weth'); + + // Deploy Comet + const deployed = await deployComet(deploymentManager, deploySpec); + + return { + ...deployed, + bulker, + rewards, + COMP + }; +} \ No newline at end of file diff --git a/deployments/mainnet/usds/migrations/1729069465_configurate_and_ens.ts b/deployments/mainnet/usds/migrations/1729069465_configurate_and_ens.ts new file mode 100644 index 000000000..0512a7db6 --- /dev/null +++ b/deployments/mainnet/usds/migrations/1729069465_configurate_and_ens.ts @@ -0,0 +1,279 @@ +import { ethers, utils } from 'ethers'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { exp, getConfigurationStruct, proposal } from '../../../../src/deploy'; +import { expect } from 'chai'; + +const ENSName = 'compound-community-licenses.eth'; +const ENSResolverAddress = '0x4976fb03C32e5B8cfe2b6cCB31c09Ba78EBaBa41'; +const ENSRegistryAddress = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e'; +const ENSSubdomainLabel = 'v3-additional-grants'; +const ENSSubdomain = `${ENSSubdomainLabel}.${ENSName}`; +const ENSTextRecordKey = 'v3-official-markets'; + +const USDSAmount = ethers.BigNumber.from(exp(300_000, 18)); +const cDAIAddress = '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643'; +const DaiToUsdsConverterAddress = '0x3225737a9Bbb6473CB4a45b7244ACa2BeFdB276A'; +const DAIAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; + +export default migration('1729069465_configurate_and_ens', { + async prepare() { + return {}; + }, + + async enact(deploymentManager: DeploymentManager, _) { + const trace = deploymentManager.tracer(); + + const { + comet, + cometAdmin, + configurator, + rewards, + COMP, + governor + } = await deploymentManager.getContracts(); + + const configuration = await getConfigurationStruct(deploymentManager); + const cometFactory = await deploymentManager.fromDep('cometFactory', 'mainnet', 'usdt', true); + + const ENSResolver = await deploymentManager.existing('ENSResolver', ENSResolverAddress); + const subdomainHash = ethers.utils.namehash(ENSSubdomain); + const currentChainId = 1; + const newMarketObject = { baseSymbol: 'USDS', cometAddress: comet.address }; + const officialMarketsJSON = JSON.parse(await ENSResolver.text(subdomainHash, ENSTextRecordKey)); + + if (officialMarketsJSON[currentChainId]) { + officialMarketsJSON[currentChainId].push(newMarketObject); + } else { + officialMarketsJSON[currentChainId] = [newMarketObject]; + } + + const _reduceReservesCalldata = utils.defaultAbiCoder.encode( + ['uint256'], + [USDSAmount] + ); + + const approveCalldata = utils.defaultAbiCoder.encode( + ['address', 'uint256'], + [DaiToUsdsConverterAddress, USDSAmount] + ); + + const convertCalldata = utils.defaultAbiCoder.encode( + ['address', 'uint256'], + [comet.address, USDSAmount] + ); + + const actions = [ + // 1. Set the Comet factory in configuration + { + contract: configurator, + signature: 'setFactory(address,address)', + args: [comet.address, cometFactory.address], + }, + // 2. Set the Comet configuration + { + contract: configurator, + signature: '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)[]))', + args: [comet.address, configuration], + }, + // 3. Deploy Comet and upgrade it to the new implementation + { + contract: cometAdmin, + signature: 'deployAndUpgradeTo(address,address)', + args: [configurator.address, comet.address], + }, + // 4. Set the reward configuration + { + contract: rewards, + signature: 'setRewardConfig(address,address)', + args: [comet.address, COMP.address], + }, + + // 5. Get DAI reserves from cDAI contract + { + target: cDAIAddress, + signature: '_reduceReserves(uint256)', + calldata: _reduceReservesCalldata + }, + // 6. Approve DAI to the Comet contract + { + target: DAIAddress, + signature: 'approve(address,uint256)', + calldata: approveCalldata, + }, + // 7. Convert DAI to USDS + { + target: DaiToUsdsConverterAddress, + signature: 'daiToUsds(address,uint256)', + calldata: convertCalldata + }, + // 8. 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 = '# Initialize cUSDSv3 on Ethereum Mainnet\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes the deployment of Compound III to the Mainnet network. This proposal takes the governance steps recommended and necessary to initialize a Compound III USDS market on Mainnet; upon execution, cUSDSv3 will be ready for use. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based off of the [recommendations from Gauntlet](https://www.comp.xyz/t/add-collateral-usds-market-on-eth-mainnet/5781/5).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/942), [deploy market GitHub action run](https://github.com/woof-software/comet/actions/runs/11392132048) and [forum discussion](https://www.comp.xyz/t/add-collateral-usds-market-on-eth-mainnet/5781).\n\n\n## wstETH price feed\n\nFor LSTs, the goal is to use full exchange rate price feeds. Thus, we treat that stETH:ETH is 1:1.\n\n## sUSDS and sUSDe collaterals\n\nGauntlet suggests having sUDSS and sUSDe collaterals. This proposal does not include them, because the price feed is under the audit. We suggest to start bootstrapping the liquidity without these collaterals and as the price feed is audited, we will add these collaterals. We discussed it with Gauntlet, and we received the approval to have such an approach.\n\n## Proposal Actions\n\nThe first proposal action sets the CometFactory for the new Comet instance in the existing Configurator.\n\nThe second action configures the Comet instance in the Configurator.\n\nThe third action deploys an instance of the newly configured factory and upgrades the Comet instance to use that implementation.\n\nThe fourth action configures the existing rewards contract for the newly deployed Comet instance.\n\nThe fifth action reduces Compound’s [cDAI](https://etherscan.io/address/0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643) reserves and transfers it to Timelock, in order to convert it to USDS to then seed the market reserves for the cUSDSv3 Comet.\n\nThe sixth action approves DAI to DAI-to-USDS native converter.\n\nThe seventh action converts DAI to USDS with 1:1 ratio and transfers USDS to cUSDSv3 Comet.\n\nThe eight action updates the ENS TXT record `v3-official-markets` on `v3-additional-grants.compound-community-licenses.eth`, updating the official markets JSON to include the new Ethereum Mainnet cUSDSv3 market.'; + const txn = await deploymentManager.retry( + async () => trace((await governor.propose(...await proposal(actions, 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) { + + const { + comet, + rewards, + timelock, + WETH, + wstETH, + cbBTC, + tBTC, + USDe, + COMP + } = await deploymentManager.getContracts(); + + // 1. & 2. & 3. + const cbbtcInfo = await comet.getAssetInfoByAddress(cbBTC.address); + const tbtcInfo = await comet.getAssetInfoByAddress(tBTC.address); + const wethInfo = await comet.getAssetInfoByAddress(WETH.address); + const usdeInfo = await comet.getAssetInfoByAddress(USDe.address); + const wstETHInfo = await comet.getAssetInfoByAddress(wstETH.address); + + expect(cbbtcInfo.supplyCap).to.be.eq(exp(150, 8)); + expect(tbtcInfo.supplyCap).to.be.eq(exp(285, 18)); + expect(wethInfo.supplyCap).to.be.eq(exp(50_000, 18)); + expect(usdeInfo.supplyCap).to.be.eq(exp(50_000_000, 18)); + expect(wstETHInfo.supplyCap).to.be.eq(exp(10_000, 18)); + + expect(await comet.baseTrackingSupplySpeed()).to.be.equal(exp(25 / 86400, 15, 18)); // 289351851851 + expect(await comet.baseTrackingBorrowSpeed()).to.be.equal(exp(25 / 86400, 15, 18)); // 289351851851 + + // 4 + 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); + + expect((await comet.pauseGuardian()).toLowerCase()).to.be.eq('0xbbf3f1421d886e9b2c5d716b5192ac998af2012c'); + + // 5. & 6. + expect(await comet.getReserves()).to.be.equal(USDSAmount); + + // 7. + const ENSResolver = await deploymentManager.existing('ENSResolver', ENSResolverAddress); + const ENSRegistry = await deploymentManager.existing('ENSRegistry', ENSRegistryAddress); + const subdomainHash = ethers.utils.namehash(ENSSubdomain); + const officialMarketsJSON = await ENSResolver.text(subdomainHash, ENSTextRecordKey); + expect(await ENSRegistry.recordExists(subdomainHash)).to.be.equal(true); + expect(await ENSRegistry.owner(subdomainHash)).to.be.equal(timelock.address); + expect(await ENSRegistry.resolver(subdomainHash)).to.be.equal(ENSResolverAddress); + expect(await ENSRegistry.ttl(subdomainHash)).to.be.equal(0); + + const officialMarkets = JSON.parse(officialMarketsJSON); + expect(officialMarkets).to.deep.equal({ + 1: [ + { + baseSymbol: 'USDC', + cometAddress: '0xc3d688B66703497DAA19211EEdff47f25384cdc3', + }, + { + baseSymbol: 'WETH', + cometAddress: '0xA17581A9E3356d9A858b789D68B4d866e593aE94', + }, + { + baseSymbol: 'USDT', + cometAddress: '0x3Afdc9BCA9213A35503b077a6072F3D0d5AB0840' + }, + { + baseSymbol: 'wstETH', + cometAddress: '0x3D0bb1ccaB520A66e607822fC55BC921738fAFE3', + }, + { + baseSymbol: 'USDS', + cometAddress: comet.address, + } + ], + 10: [ + { + baseSymbol: 'USDC', + cometAddress: '0x2e44e174f7D53F0212823acC11C01A11d58c5bCB', + }, + { + baseSymbol: 'USDT', + cometAddress: '0x995E394b8B2437aC8Ce61Ee0bC610D617962B214', + }, + { + baseSymbol: 'WETH', + cometAddress: '0xE36A30D249f7761327fd973001A32010b521b6Fd' + } + ], + 137: [ + { + baseSymbol: 'USDC', + cometAddress: '0xF25212E676D1F7F89Cd72fFEe66158f541246445', + }, + { + baseSymbol: 'USDT', + cometAddress: '0xaeB318360f27748Acb200CE616E389A6C9409a07', + }, + ], + 8453: [ + { + baseSymbol: 'USDbC', + cometAddress: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf', + }, + { + baseSymbol: 'WETH', + cometAddress: '0x46e6b214b524310239732D51387075E0e70970bf', + }, + { + baseSymbol: 'USDC', + cometAddress: '0xb125E6687d4313864e53df431d5425969c15Eb2F', + }, + { + baseSymbol: 'AERO', + cometAddress: '0x784efeB622244d2348d4F2522f8860B96fbEcE89' + } + ], + 42161: [ + { + baseSymbol: 'USDC.e', + cometAddress: '0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA', + }, + { + baseSymbol: 'USDC', + cometAddress: '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf', + }, + { + baseSymbol: 'WETH', + cometAddress: '0x6f7D514bbD4aFf3BcD1140B7344b32f063dEe486', + }, + { + baseSymbol: 'USDT', + cometAddress: '0xd98Be00b5D27fc98112BdE293e487f8D4cA57d07', + }, + ], + 534352: [ + { + baseSymbol: 'USDC', + cometAddress: '0xB2f97c1Bd3bf02f5e74d13f02E3e26F93D77CE44', + }, + ], + }); + }, +}); \ No newline at end of file diff --git a/deployments/mainnet/usds/relations.ts b/deployments/mainnet/usds/relations.ts new file mode 100644 index 000000000..7b0cc418d --- /dev/null +++ b/deployments/mainnet/usds/relations.ts @@ -0,0 +1,25 @@ +import { RelationConfigMap } from '../../../plugins/deployment_manager/RelationConfig'; +import baseRelationConfig from '../../relations'; + +export default { + ...baseRelationConfig, + 'wstETH': { + artifact: 'contracts/bulkers/IWstETH.sol', + relations: { + stETH: { + field: async (wstETH) => wstETH.stETH() + } + } + }, + 'AppProxyUpgradeable': { + artifact: 'contracts/ERC20.sol:ERC20', + }, + 'ERC1967Proxy': { + artifact: 'contracts/ERC20.sol:ERC20', + delegates: { + field: { + slot: '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc' + } + } + }, +}; \ No newline at end of file diff --git a/deployments/mainnet/usds/roots.json b/deployments/mainnet/usds/roots.json new file mode 100644 index 000000000..565665908 --- /dev/null +++ b/deployments/mainnet/usds/roots.json @@ -0,0 +1,7 @@ +{ + "comet": "0x5D409e56D886231aDAf00c8775665AD0f9897b56", + "configurator": "0x316f9708bB98af7dA9c68C1C3b5e79039cD336E3", + "rewards": "0x1B0e765F6224C21223AeA2af16c1C46E38885a40", + "bulker": "0xa397a8C2086C554B531c02E29f3291c9704B00c7", + "COMP": "0xc00e94Cb662C3520282E6f5717214004A7f26888" +} \ No newline at end of file diff --git a/deployments/mainnet/usdt/migrations/1726218459_add_cbbtc_as_collateral.ts b/deployments/mainnet/usdt/migrations/1726218459_add_cbbtc_as_collateral.ts new file mode 100644 index 000000000..c5ed9978c --- /dev/null +++ b/deployments/mainnet/usdt/migrations/1726218459_add_cbbtc_as_collateral.ts @@ -0,0 +1,125 @@ +import { expect } from 'chai'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { exp, proposal } from '../../../../src/deploy'; + +const CBBTC_ADDRESS = '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf'; +const CBBTC_USD_PRICE_FEED = '0x2665701293fCbEB223D11A08D826563EDcCE423A'; + +let priceFeedAddress: string; + +export default migration('1726218459_add_cbbtc_as_collateral', { + async prepare(deploymentManager: DeploymentManager) { + const _cbBTCPriceFeed = await deploymentManager.deploy( + 'cbBTC:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + CBBTC_USD_PRICE_FEED, // cbBTC / USD price feed + 8 // decimals + ] + ); + return { cbBTCPriceFeedAddress: _cbBTCPriceFeed.address }; + }, + + enact: async (deploymentManager: DeploymentManager, _, { cbBTCPriceFeedAddress }) => { + const trace = deploymentManager.tracer(); + + const cbBTC = await deploymentManager.existing( + 'cbBTC', + CBBTC_ADDRESS, + 'mainnet', + 'contracts/ERC20.sol:ERC20' + ); + const cbBTCPriceFeed = await deploymentManager.existing( + 'cbBTC:priceFeed', + cbBTCPriceFeedAddress, + 'mainnet' + ); + priceFeedAddress = cbBTCPriceFeed.address; + const { + governor, + comet, + cometAdmin, + configurator + } = await deploymentManager.getContracts(); + + const newAssetConfig = { + asset: cbBTC.address, + priceFeed: cbBTCPriceFeed.address, + decimals: await cbBTC.decimals(), + borrowCollateralFactor: exp(0.8, 18), + liquidateCollateralFactor: exp(0.85, 18), + liquidationFactor: exp(0.95, 18), + supplyCap: exp(93, 8), + }; + + const mainnetActions = [ + // 1. Add cbBTC as asset + { + contract: configurator, + signature: 'addAsset(address,(address,address,uint8,uint64,uint64,uint64,uint128))', + args: [comet.address, newAssetConfig], + }, + // 2. Deploy and upgrade to a new version of Comet + { + contract: cometAdmin, + signature: 'deployAndUpgradeTo(address,address)', + args: [configurator.address, comet.address], + }, + ]; + + const description = '# Add cbBTC as collateral into cUSDTv3 on Ethereum\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes to add cbBTC into cUSDTv3 on Ethereum network. This proposal takes the governance steps recommended and necessary to update a Compound III USDT market on Ethereum. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based off of the [recommendations from Gauntlet](https://www.comp.xyz/t/add-collateral-cbbtc-to-weth-market-on-base-and-mainnet/5689/2).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/922) and [forum discussion](https://www.comp.xyz/t/add-collateral-cbbtc-to-weth-market-on-base-and-mainnet/5689).\n\n\n## Proposal Actions\n\nThe first proposal action adds cbBTC asset as collateral with corresponding configurations.\n\nThe second action deploys and upgrades Comet to a new version.'; + const txn = await deploymentManager.retry(async () => + trace( + await governor.propose(...(await proposal(mainnetActions, description))) + ) + ); + + const event = txn.events.find( + (event) => event.event === 'ProposalCreated' + ); + const [proposalId] = event.args; + trace(`Created proposal ${proposalId}.`); + }, + + async enacted(): Promise { + return false; + }, + + async verify(deploymentManager: DeploymentManager) { + const { comet, configurator } = await deploymentManager.getContracts(); + + const cbBTCAssetIndex = Number(await comet.numAssets()) - 1; + + const cbBTCAssetConfig = { + asset: CBBTC_ADDRESS, + priceFeed: priceFeedAddress, + decimals: 8, + borrowCollateralFactor: exp(0.8, 18), + liquidateCollateralFactor: exp(0.85, 18), + liquidationFactor: exp(0.95, 18), + supplyCap: exp(93, 8), + }; + + // 1. Compare proposed asset config with Comet asset info + const cbBTCAssetInfo = await comet.getAssetInfoByAddress(CBBTC_ADDRESS); + expect(cbBTCAssetIndex).to.be.equal(cbBTCAssetInfo.offset); + expect(cbBTCAssetConfig.asset).to.be.equal(cbBTCAssetInfo.asset); + expect(cbBTCAssetConfig.priceFeed).to.be.equal(cbBTCAssetInfo.priceFeed); + expect(exp(1, cbBTCAssetConfig.decimals)).to.be.equal(cbBTCAssetInfo.scale); + expect(cbBTCAssetConfig.borrowCollateralFactor).to.be.equal(cbBTCAssetInfo.borrowCollateralFactor); + expect(cbBTCAssetConfig.liquidateCollateralFactor).to.be.equal(cbBTCAssetInfo.liquidateCollateralFactor); + expect(cbBTCAssetConfig.liquidationFactor).to.be.equal(cbBTCAssetInfo.liquidationFactor); + expect(cbBTCAssetConfig.supplyCap).to.be.equal(cbBTCAssetInfo.supplyCap); + + // 2. Compare proposed asset config with Configurator asset config + const configuratorcbBTCAssetConfig = (await configurator.getConfiguration(comet.address)).assetConfigs[cbBTCAssetIndex]; + expect(cbBTCAssetConfig.asset).to.be.equal(configuratorcbBTCAssetConfig.asset); + expect(cbBTCAssetConfig.priceFeed).to.be.equal(configuratorcbBTCAssetConfig.priceFeed); + expect(cbBTCAssetConfig.decimals).to.be.equal(configuratorcbBTCAssetConfig.decimals); + expect(cbBTCAssetConfig.borrowCollateralFactor).to.be.equal(configuratorcbBTCAssetConfig.borrowCollateralFactor); + expect(cbBTCAssetConfig.liquidateCollateralFactor).to.be.equal(configuratorcbBTCAssetConfig.liquidateCollateralFactor); + expect(cbBTCAssetConfig.liquidationFactor).to.be.equal(configuratorcbBTCAssetConfig.liquidationFactor); + expect(cbBTCAssetConfig.supplyCap).to.be.equal(configuratorcbBTCAssetConfig.supplyCap); + }, +}); diff --git a/deployments/mainnet/usdt/migrations/1728054806_add_tbtc_collateral.ts b/deployments/mainnet/usdt/migrations/1728054806_add_tbtc_collateral.ts new file mode 100644 index 000000000..095dd9734 --- /dev/null +++ b/deployments/mainnet/usdt/migrations/1728054806_add_tbtc_collateral.ts @@ -0,0 +1,133 @@ +import { expect } from 'chai'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { exp, proposal } from '../../../../src/deploy'; + +const TBTC_ADDRESS = '0x18084fbA666a33d37592fA2633fD49a74DD93a88'; +const TBTC_TO_USD_PRICE_FEED = '0x8350b7De6a6a2C1368E7D4Bd968190e13E354297'; + +let newPriceFeedAddress: string; + +export default migration('1728054806_add_tbtc_collateral', { + async prepare(deploymentManager: DeploymentManager) { + const tBTCMultiplicativePriceFeed = await deploymentManager.deploy( + 'tBTC:priceFeed', + 'pricefeeds/ScalingPriceFeed.sol', + [ + TBTC_TO_USD_PRICE_FEED, // tBTC / USD price feed + 8, // decimals + ] + ); + return { tBTCPriceFeedAddress: tBTCMultiplicativePriceFeed.address }; + }, + + async enact(deploymentManager: DeploymentManager, _, { tBTCPriceFeedAddress }) { + + const trace = deploymentManager.tracer(); + + const tBTC = await deploymentManager.existing( + 'tBTC', + TBTC_ADDRESS, + 'mainnet', + 'contracts/ERC20.sol:ERC20' + ); + const tBTCPriceFeed = await deploymentManager.existing( + 'tBTC:priceFeed', + tBTCPriceFeedAddress, + 'mainnet' + ); + + newPriceFeedAddress = tBTCPriceFeedAddress; + + const { + governor, + comet, + cometAdmin, + configurator, + } = await deploymentManager.getContracts(); + + const tBTCAssetConfig = { + asset: tBTC.address, + priceFeed: tBTCPriceFeed.address, + decimals: await tBTC.decimals(), + borrowCollateralFactor: exp(0.76, 18), + liquidateCollateralFactor: exp(0.81, 18), + liquidationFactor: exp(0.9, 18), + supplyCap: exp(285, 18), + }; + + const mainnetActions = [ + // 1. Add tBTC as asset + { + contract: configurator, + signature: 'addAsset(address,(address,address,uint8,uint64,uint64,uint64,uint128))', + args: [comet.address, tBTCAssetConfig], + }, + // 2. Deploy and upgrade to a new version of Comet + { + contract: cometAdmin, + signature: 'deployAndUpgradeTo(address,address)', + args: [configurator.address, comet.address], + }, + ]; + + const description = '# Add tBTC as collateral into cUSDTv3 on Mainnet\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes to add tBTC into cUSDTv3 on Ethereum network. This proposal takes the governance steps recommended and necessary to update a Compound III USDT market on Ethereum. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based on the [recommendations from Gauntlet](https://www.comp.xyz/t/add-collateral-tbtc-to-eth-market-on-mainnet/5399/12).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/936) and [forum discussion](https://www.comp.xyz/t/add-collateral-tbtc-to-eth-market-on-mainnet/5399).\n\n\n## Proposal Actions\n\nThe first action adds tBTC asset as collateral with corresponding configurations.\n\nThe second action deploys and upgrades Comet to a new version.'; + const txn = await deploymentManager.retry(async () => + trace( + await governor.propose(...(await proposal(mainnetActions, description))) + ) + ); + + const event = txn.events.find( + (event) => event.event === 'ProposalCreated' + ); + const [proposalId] = event.args; + trace(`Created proposal ${proposalId}.`); + }, + + async enacted(): Promise { + return false; + }, + + async verify(deploymentManager: DeploymentManager) { + const { comet, configurator } = await deploymentManager.getContracts(); + + const tBTCAssetIndex = Number(await comet.numAssets()) - 1; + + const tBTC = await deploymentManager.existing( + 'tBTC', + TBTC_ADDRESS, + 'mainnet', + 'contracts/ERC20.sol:ERC20' + ); + const tBTCAssetConfig = { + asset: tBTC.address, + priceFeed: newPriceFeedAddress, + decimals: await tBTC.decimals(), + borrowCollateralFactor: exp(0.76, 18), + liquidateCollateralFactor: exp(0.81, 18), + liquidationFactor: exp(0.9, 18), + supplyCap: exp(285, 18), + }; + + // 1. Compare tBTC asset config with Comet and Configurator asset info + const cometTBTCAssetInfo = await comet.getAssetInfoByAddress(TBTC_ADDRESS); + expect(tBTCAssetIndex).to.be.equal(cometTBTCAssetInfo.offset); + expect(tBTCAssetConfig.asset).to.be.equal(cometTBTCAssetInfo.asset); + expect(tBTCAssetConfig.priceFeed).to.be.equal(cometTBTCAssetInfo.priceFeed); + expect(exp(1, tBTCAssetConfig.decimals)).to.be.equal(cometTBTCAssetInfo.scale); + expect(tBTCAssetConfig.borrowCollateralFactor).to.be.equal(cometTBTCAssetInfo.borrowCollateralFactor); + expect(tBTCAssetConfig.liquidateCollateralFactor).to.be.equal(cometTBTCAssetInfo.liquidateCollateralFactor); + expect(tBTCAssetConfig.liquidationFactor).to.be.equal(cometTBTCAssetInfo.liquidationFactor); + expect(tBTCAssetConfig.supplyCap).to.be.equal(cometTBTCAssetInfo.supplyCap); + + const configuratorTBTCAssetConfig = (await configurator.getConfiguration(comet.address)).assetConfigs[tBTCAssetIndex]; + expect(tBTCAssetConfig.asset).to.be.equal(configuratorTBTCAssetConfig.asset); + expect(tBTCAssetConfig.priceFeed).to.be.equal(configuratorTBTCAssetConfig.priceFeed); + expect(tBTCAssetConfig.decimals).to.be.equal(configuratorTBTCAssetConfig.decimals); + expect(tBTCAssetConfig.borrowCollateralFactor).to.be.equal(configuratorTBTCAssetConfig.borrowCollateralFactor); + expect(tBTCAssetConfig.liquidateCollateralFactor).to.be.equal(configuratorTBTCAssetConfig.liquidateCollateralFactor); + expect(tBTCAssetConfig.liquidationFactor).to.be.equal(configuratorTBTCAssetConfig.liquidationFactor); + expect(tBTCAssetConfig.supplyCap).to.be.equal(configuratorTBTCAssetConfig.supplyCap); + }, +}); \ No newline at end of file diff --git a/deployments/mainnet/weth/migrations/1726228702_add_cbbtc_as_collateral.ts b/deployments/mainnet/weth/migrations/1726228702_add_cbbtc_as_collateral.ts new file mode 100644 index 000000000..c84bae952 --- /dev/null +++ b/deployments/mainnet/weth/migrations/1726228702_add_cbbtc_as_collateral.ts @@ -0,0 +1,128 @@ +import { expect } from 'chai'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { exp, proposal } from '../../../../src/deploy'; + +const CBBTC_ADDRESS = '0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf'; +const CBBTC_USD_PRICE_FEED = '0x2665701293fCbEB223D11A08D826563EDcCE423A'; +const ETH_USD_PRICE_FEED_ADDRESS = '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419'; + +let priceFeedAddress: string; + +export default migration('1726228702_add_cbbtc_as_collateral', { + async prepare(deploymentManager: DeploymentManager) { + const _cbBTCPriceFeed = await deploymentManager.deploy( + 'cbBTC:priceFeed', + 'pricefeeds/ReverseMultiplicativePriceFeed.sol', + [ + CBBTC_USD_PRICE_FEED, // cbBTC / USD price feed + ETH_USD_PRICE_FEED_ADDRESS, // USD / ETH price feed + 8, // decimals + 'cbBTC / ETH price feed', // description + ] + ); + return { cbBTCPriceFeedAddress: _cbBTCPriceFeed.address }; + }, + + enact: async (deploymentManager: DeploymentManager, _, { cbBTCPriceFeedAddress }) => { + const trace = deploymentManager.tracer(); + + const cbBTC = await deploymentManager.existing( + 'cbBTC', + CBBTC_ADDRESS, + 'mainnet', + 'contracts/ERC20.sol:ERC20' + ); + const cbBTCPriceFeed = await deploymentManager.existing( + 'cbBTC:priceFeed', + cbBTCPriceFeedAddress, + 'mainnet' + ); + priceFeedAddress = cbBTCPriceFeed.address; + const { + governor, + comet, + cometAdmin, + configurator + } = await deploymentManager.getContracts(); + + const newAssetConfig = { + asset: cbBTC.address, + priceFeed: cbBTCPriceFeed.address, + decimals: await cbBTC.decimals(), + borrowCollateralFactor: exp(0.8, 18), + liquidateCollateralFactor: exp(0.85, 18), + liquidationFactor: exp(0.95, 18), + supplyCap: exp(93, 8), + }; + + const mainnetActions = [ + // 1. Add cbBTC as asset + { + contract: configurator, + signature: 'addAsset(address,(address,address,uint8,uint64,uint64,uint64,uint128))', + args: [comet.address, newAssetConfig], + }, + // 2. Deploy and upgrade to a new version of Comet + { + contract: cometAdmin, + signature: 'deployAndUpgradeTo(address,address)', + args: [configurator.address, comet.address], + }, + ]; + + const description = '# Add cbBTC as collateral into cWETHv3 on Ethereum\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes to add cbBTC into cWETHv3 on Ethereum network. This proposal takes the governance steps recommended and necessary to update a Compound III WETH market on Ethereum. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based off of the [recommendations from Gauntlet](https://www.comp.xyz/t/add-collateral-cbbtc-to-weth-market-on-base-and-mainnet/5689/2).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/923) and [forum discussion](https://www.comp.xyz/t/add-collateral-cbbtc-to-weth-market-on-base-and-mainnet/5689).\n\n\n## Proposal Actions\n\nThe first proposal action adds cbBTC asset as collateral with corresponding configurations.\n\nThe second action deploys and upgrades Comet to a new version.'; + const txn = await deploymentManager.retry(async () => + trace( + await governor.propose(...(await proposal(mainnetActions, description))) + ) + ); + + const event = txn.events.find( + (event) => event.event === 'ProposalCreated' + ); + const [proposalId] = event.args; + trace(`Created proposal ${proposalId}.`); + }, + + async enacted(): Promise { + return false; + }, + + async verify(deploymentManager: DeploymentManager) { + const { comet, configurator } = await deploymentManager.getContracts(); + + const cbBTCAssetIndex = Number(await comet.numAssets()) - 1; + + const cbBTCAssetConfig = { + asset: CBBTC_ADDRESS, + priceFeed: priceFeedAddress, + decimals: 8, + borrowCollateralFactor: exp(0.8, 18), + liquidateCollateralFactor: exp(0.85, 18), + liquidationFactor: exp(0.95, 18), + supplyCap: exp(93, 8), + }; + + // 1. Compare proposed asset config with Comet asset info + const cbBTCAssetInfo = await comet.getAssetInfoByAddress(CBBTC_ADDRESS); + expect(cbBTCAssetIndex).to.be.equal(cbBTCAssetInfo.offset); + expect(cbBTCAssetConfig.asset).to.be.equal(cbBTCAssetInfo.asset); + expect(cbBTCAssetConfig.priceFeed).to.be.equal(cbBTCAssetInfo.priceFeed); + expect(exp(1, cbBTCAssetConfig.decimals)).to.be.equal(cbBTCAssetInfo.scale); + expect(cbBTCAssetConfig.borrowCollateralFactor).to.be.equal(cbBTCAssetInfo.borrowCollateralFactor); + expect(cbBTCAssetConfig.liquidateCollateralFactor).to.be.equal(cbBTCAssetInfo.liquidateCollateralFactor); + expect(cbBTCAssetConfig.liquidationFactor).to.be.equal(cbBTCAssetInfo.liquidationFactor); + expect(cbBTCAssetConfig.supplyCap).to.be.equal(cbBTCAssetInfo.supplyCap); + + // 2. Compare proposed asset config with Configurator asset config + const configuratorcbBTCAssetConfig = (await configurator.getConfiguration(comet.address)).assetConfigs[cbBTCAssetIndex]; + expect(cbBTCAssetConfig.asset).to.be.equal(configuratorcbBTCAssetConfig.asset); + expect(cbBTCAssetConfig.priceFeed).to.be.equal(configuratorcbBTCAssetConfig.priceFeed); + expect(cbBTCAssetConfig.decimals).to.be.equal(configuratorcbBTCAssetConfig.decimals); + expect(cbBTCAssetConfig.borrowCollateralFactor).to.be.equal(configuratorcbBTCAssetConfig.borrowCollateralFactor); + expect(cbBTCAssetConfig.liquidateCollateralFactor).to.be.equal(configuratorcbBTCAssetConfig.liquidateCollateralFactor); + expect(cbBTCAssetConfig.liquidationFactor).to.be.equal(configuratorcbBTCAssetConfig.liquidationFactor); + expect(cbBTCAssetConfig.supplyCap).to.be.equal(configuratorcbBTCAssetConfig.supplyCap); + }, +}); diff --git a/deployments/mainnet/weth/migrations/1727880771_add_tbtc_collateral.ts b/deployments/mainnet/weth/migrations/1727880771_add_tbtc_collateral.ts new file mode 100644 index 000000000..a60516a20 --- /dev/null +++ b/deployments/mainnet/weth/migrations/1727880771_add_tbtc_collateral.ts @@ -0,0 +1,136 @@ +import { expect } from 'chai'; +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { exp, proposal } from '../../../../src/deploy'; + +const TBTC_ADDRESS = '0x18084fbA666a33d37592fA2633fD49a74DD93a88'; +const TBTC_TO_USD_PRICE_FEED = '0x8350b7De6a6a2C1368E7D4Bd968190e13E354297'; +const ETH_TO_USD_PRICE_FEED = '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419'; + +let newPriceFeedAddress: string; + +export default migration('1727880771_add_tbtc_collateral', { + async prepare(deploymentManager: DeploymentManager) { + const tBTCMultiplicativePriceFeed = await deploymentManager.deploy( + 'tBTC:priceFeed', + 'pricefeeds/ReverseMultiplicativePriceFeed.sol', + [ + TBTC_TO_USD_PRICE_FEED, // tBTC / USD price feed + ETH_TO_USD_PRICE_FEED, // ETH / USD (reversed) price feed + 8, // decimals + 'tBTC / ETH price feed' // description + ] + ); + return { tBTCPriceFeedAddress: tBTCMultiplicativePriceFeed.address }; + }, + + async enact(deploymentManager: DeploymentManager, _, { tBTCPriceFeedAddress }) { + + const trace = deploymentManager.tracer(); + + const tBTC = await deploymentManager.existing( + 'tBTC', + TBTC_ADDRESS, + 'mainnet', + 'contracts/ERC20.sol:ERC20' + ); + const tBTCPriceFeed = await deploymentManager.existing( + 'tBTC:priceFeed', + tBTCPriceFeedAddress, + 'mainnet' + ); + + newPriceFeedAddress = tBTCPriceFeedAddress; + + const { + governor, + comet, + cometAdmin, + configurator, + } = await deploymentManager.getContracts(); + + const tBTCAssetConfig = { + asset: tBTC.address, + priceFeed: tBTCPriceFeed.address, + decimals: await tBTC.decimals(), + borrowCollateralFactor: exp(0.76, 18), + liquidateCollateralFactor: exp(0.81, 18), + liquidationFactor: exp(0.9, 18), + supplyCap: exp(315, 18), + }; + + const mainnetActions = [ + // 1. Add tBTC as asset + { + contract: configurator, + signature: 'addAsset(address,(address,address,uint8,uint64,uint64,uint64,uint128))', + args: [comet.address, tBTCAssetConfig], + }, + // 2. Deploy and upgrade to a new version of Comet + { + contract: cometAdmin, + signature: 'deployAndUpgradeTo(address,address)', + args: [configurator.address, comet.address], + }, + ]; + + const description = '# Add tBTC as collateral into cWETHv3 on Mainnet\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes to add tBTC into cWETHv3 on Ethereum network. This proposal takes the governance steps recommended and necessary to update a Compound III WETH market on Ethereum. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based on the [recommendations from Gauntlet](https://www.comp.xyz/t/add-collateral-tbtc-to-eth-market-on-mainnet/5399/12).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/934) and [forum discussion](https://www.comp.xyz/t/add-collateral-tbtc-to-eth-market-on-mainnet/5399).\n\n\n## Proposal Actions\n\nThe first action adds tBTC asset as collateral with corresponding configurations.\n\nThe second action deploys and upgrades Comet to a new version.'; + const txn = await deploymentManager.retry(async () => + trace( + await governor.propose(...(await proposal(mainnetActions, 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) { + const { comet, configurator } = await deploymentManager.getContracts(); + + const tBTCAssetIndex = Number(await comet.numAssets()) - 1; + + const tBTC = await deploymentManager.existing( + 'tBTC', + TBTC_ADDRESS, + 'mainnet', + 'contracts/ERC20.sol:ERC20' + ); + const tBTCAssetConfig = { + asset: tBTC.address, + priceFeed: newPriceFeedAddress, + decimals: await tBTC.decimals(), + borrowCollateralFactor: exp(0.76, 18), + liquidateCollateralFactor: exp(0.81, 18), + liquidationFactor: exp(0.9, 18), + supplyCap: exp(315, 18), + }; + + // 1. Compare tBTC asset config with Comet and Configurator asset info + const cometTBTCAssetInfo = await comet.getAssetInfoByAddress(TBTC_ADDRESS); + expect(tBTCAssetIndex).to.be.equal(cometTBTCAssetInfo.offset); + expect(tBTCAssetConfig.asset).to.be.equal(cometTBTCAssetInfo.asset); + expect(tBTCAssetConfig.priceFeed).to.be.equal(cometTBTCAssetInfo.priceFeed); + expect(exp(1, tBTCAssetConfig.decimals)).to.be.equal(cometTBTCAssetInfo.scale); + expect(tBTCAssetConfig.borrowCollateralFactor).to.be.equal(cometTBTCAssetInfo.borrowCollateralFactor); + expect(tBTCAssetConfig.liquidateCollateralFactor).to.be.equal(cometTBTCAssetInfo.liquidateCollateralFactor); + expect(tBTCAssetConfig.liquidationFactor).to.be.equal(cometTBTCAssetInfo.liquidationFactor); + expect(tBTCAssetConfig.supplyCap).to.be.equal(cometTBTCAssetInfo.supplyCap); + + const configuratorTBTCAssetConfig = (await configurator.getConfiguration(comet.address)).assetConfigs[tBTCAssetIndex]; + expect(tBTCAssetConfig.asset).to.be.equal(configuratorTBTCAssetConfig.asset); + expect(tBTCAssetConfig.priceFeed).to.be.equal(configuratorTBTCAssetConfig.priceFeed); + expect(tBTCAssetConfig.decimals).to.be.equal(configuratorTBTCAssetConfig.decimals); + expect(tBTCAssetConfig.borrowCollateralFactor).to.be.equal(configuratorTBTCAssetConfig.borrowCollateralFactor); + expect(tBTCAssetConfig.liquidateCollateralFactor).to.be.equal(configuratorTBTCAssetConfig.liquidateCollateralFactor); + expect(tBTCAssetConfig.liquidationFactor).to.be.equal(configuratorTBTCAssetConfig.liquidationFactor); + expect(tBTCAssetConfig.supplyCap).to.be.equal(configuratorTBTCAssetConfig.supplyCap); + }, +}); diff --git a/deployments/optimism/usdt/migrations/1713012100_configurate_and_ens.ts b/deployments/optimism/usdt/migrations/1713012100_configurate_and_ens.ts index d457597e7..86dcad052 100644 --- a/deployments/optimism/usdt/migrations/1713012100_configurate_and_ens.ts +++ b/deployments/optimism/usdt/migrations/1713012100_configurate_and_ens.ts @@ -126,7 +126,7 @@ export default migration('1713012100_configurate_and_ens', { mainnetUSDTAddress, [ 'function balanceOf(address account) external view returns (uint256)', - 'function approve(address,uint256) external' + 'function approve(address,uint256) external' ], govDeploymentManager.hre.ethers.provider ); @@ -184,10 +184,10 @@ export default migration('1713012100_configurate_and_ens', { ), }, ]; - + // the description has speeds. speeds will be set up on on-chain proposal const description = "# Initialize cUSDTv3 on Optimism\n\n## Proposal summary\n\nCompound Growth Program [AlphaGrowth] proposes the deployment of Compound III to the Optimism network. This proposal takes the governance steps recommended and necessary to initialize a Compound III USDT market on Optimism; upon execution, cUSDTv3 will be ready for use. Simulations have confirmed the market’s readiness, as much as possible, using the [Comet scenario suite](https://github.com/compound-finance/comet/tree/main/scenario). The new parameters include setting the risk parameters based on the [recommendations from Gauntlet](https://www.comp.xyz/t/deploy-compound-iii-on-optimism/4975/6).\n\nFurther detailed information can be found on the corresponding [proposal pull request](https://github.com/compound-finance/comet/pull/848) and [forum discussion](https://www.comp.xyz/t/deploy-compound-iii-on-optimism/4975).\n\n\n## Proposal Actions\n\nThe first proposal action sets the Comet configuration and deploys a new Comet implementation on Optimism. This sends the encoded `setFactory`, `setConfiguration` and `deployAndUpgradeTo` calls across the bridge to the governance receiver on Optimism. It also calls `setRewardConfig` on the Optimism rewards contract, to establish Optimism’s bridged version of COMP as the reward token for the deployment and set the initial supply speed to be 5 COMP/day and borrow speed to be 5 COMP/day.\n\nThe second action reduces Compound [cUSDT](https://etherscan.io/address/0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9) reserves to Timelock, in order to seed the market reserves through the Optimism L1StandardBridge.\n\nThe third action approves Optimism’s [L1StandardBridge](https://etherscan.io/address/0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1) to take Timelock's USDT, in order to seed the reserves through the bridge.\n\nThe fourth action deposits 10K USDT from mainnet to the Optimism L1StandardBridge contract to bridge to Comet.\n\nThe fifth action updates the ENS TXT record `v3-official-markets` on `v3-additional-grants.compound-community-licenses.eth`, updating the official markets JSON to include the new Optimism cUSDTv3 market"; - const txn = await govDeploymentManager.retry(async () =>{ + const txn = await govDeploymentManager.retry(async () => { return trace(await governor.propose(...(await proposal(actions, description)))); } ); @@ -199,7 +199,7 @@ export default migration('1713012100_configurate_and_ens', { }, async enacted(deploymentManager: DeploymentManager): Promise { - return false; + return true; }, async verify(deploymentManager: DeploymentManager, govDeploymentManager: DeploymentManager, preMigrationBlockNumber: number) { @@ -219,7 +219,7 @@ export default migration('1713012100_configurate_and_ens', { } = await govDeploymentManager.getContracts(); const stateChanges = await diffState(comet, getCometConfig, preMigrationBlockNumber); - + // uncomment on on-chain proposal PR // expect(stateChanges).to.deep.equal({ // WBTC: { diff --git a/hardhat.config.ts b/hardhat.config.ts index d15407876..f2cf294fc 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -27,6 +27,7 @@ import mainnetRelationConfigMap from './deployments/mainnet/usdc/relations'; import mainnetWethRelationConfigMap from './deployments/mainnet/weth/relations'; import mainnetUsdtRelationConfigMap from './deployments/mainnet/usdt/relations'; import mainnetWstETHRelationConfigMap from './deployments/mainnet/wsteth/relations'; +import mainnetUsdsRelationConfigMap from './deployments/mainnet/usds/relations'; import polygonRelationConfigMap from './deployments/polygon/usdc/relations'; import polygonUsdtRelationConfigMap from './deployments/polygon/usdt/relations'; import arbitrumBridgedUsdcRelationConfigMap from './deployments/arbitrum/usdc.e/relations'; @@ -38,6 +39,7 @@ import arbitrumUsdtRelationConfigMap from './deployments/arbitrum/usdt/relations import baseUsdbcRelationConfigMap from './deployments/base/usdbc/relations'; import baseWethRelationConfigMap from './deployments/base/weth/relations'; import baseUsdcRelationConfigMap from './deployments/base/usdc/relations'; +import baseAeroRelationConfigMap from './deployments/base/aero/relations'; import baseGoerliRelationConfigMap from './deployments/base-goerli/usdc/relations'; import baseGoerliWethRelationConfigMap from './deployments/base-goerli/weth/relations'; import lineaGoerliRelationConfigMap from './deployments/linea-goerli/usdc/relations'; @@ -72,7 +74,7 @@ const { REMOTE_ACCOUNTS = '' } = process.env; -function *deriveAccounts(pk: string, n: number = 10) { +function* deriveAccounts(pk: string, n: number = 10) { for (let i = 0; i < n; i++) yield (BigInt('0x' + pk) + BigInt(i)).toString(16); } @@ -188,7 +190,7 @@ function setupDefaultNetworkProviders(hardhatConfig: HardhatUserConfig) { getDefaultProviderURL(netConfig.network), gas: netConfig.gas || 'auto', gasPrice: netConfig.gasPrice || 'auto', - accounts: REMOTE_ACCOUNTS ? 'remote' : ( ETH_PK ? [...deriveAccounts(ETH_PK)] : { mnemonic: MNEMONIC } ), + accounts: REMOTE_ACCOUNTS ? 'remote' : (ETH_PK ? [...deriveAccounts(ETH_PK)] : { mnemonic: MNEMONIC }), }; } } @@ -355,7 +357,8 @@ const config: HardhatUserConfig = { usdc: mainnetRelationConfigMap, weth: mainnetWethRelationConfigMap, usdt: mainnetUsdtRelationConfigMap, - wsteth: mainnetWstETHRelationConfigMap + wsteth: mainnetWstETHRelationConfigMap, + usds: mainnetUsdsRelationConfigMap, }, polygon: { usdc: polygonRelationConfigMap, @@ -374,7 +377,8 @@ const config: HardhatUserConfig = { 'base': { usdbc: baseUsdbcRelationConfigMap, weth: baseWethRelationConfigMap, - usdc: baseUsdcRelationConfigMap + usdc: baseUsdcRelationConfigMap, + aero: baseAeroRelationConfigMap }, 'base-goerli': { usdc: baseGoerliRelationConfigMap, @@ -420,6 +424,11 @@ const config: HardhatUserConfig = { network: 'mainnet', deployment: 'wsteth' }, + { + name: 'mainnet-usds', + network: 'mainnet', + deployment: 'usds' + }, { name: 'development', network: 'hardhat', @@ -522,6 +531,12 @@ const config: HardhatUserConfig = { deployment: 'usdc', auxiliaryBase: 'mainnet' }, + { + name: 'base-aero', + network: 'base', + deployment: 'aero', + auxiliaryBase: 'mainnet' + }, { name: 'base-goerli', network: 'base-goerli', diff --git a/scenario/LiquidationBotScenario.ts b/scenario/LiquidationBotScenario.ts index 8c504b8e1..45dd6922c 100644 --- a/scenario/LiquidationBotScenario.ts +++ b/scenario/LiquidationBotScenario.ts @@ -5,6 +5,7 @@ import { ethers, event, exp, wait } from '../test/helpers'; import CometActor from './context/CometActor'; import { CometInterface, OnChainLiquidator } from '../build/types'; import { getPoolConfig, flashLoanPools } from '../scripts/liquidation_bot/liquidateUnderwaterBorrowers'; +import { getConfigForScenario } from './utils/scenarioHelper'; interface LiquidationAddresses { balancerVault: string; @@ -537,16 +538,20 @@ for (let i = 0; i < MAX_ASSETS; i++) { scenario( `LiquidationBot > absorbs, but does not attempt to purchase collateral when value is beneath liquidationThreshold`, { - filter: async (ctx) => matchesDeployment(ctx, [{ network: 'mainnet' }, { network: 'polygon' }, { network: 'arbitrum' }]) && !matchesDeployment(ctx, [{deployment: 'wsteth', network: 'mainnet'}]), - tokenBalances: { - $comet: { $base: 100000 }, - }, - cometBalances: { - albert: { - $asset0: ' == 200', - }, - betty: { $base: 1000 }, - }, + filter: async (ctx) => matchesDeployment(ctx, [{ network: 'mainnet' }, { network: 'polygon' }, { network: 'arbitrum' }]), + tokenBalances: async (ctx) => ( + { + $comet: { $base: getConfigForScenario(ctx).liquidationBase }, + } + ), + cometBalances: async (ctx) => ( + { + albert: { + $asset0: ` == ${getConfigForScenario(ctx).liquidationAsset}`, + }, + betty: { $base: getConfigForScenario(ctx).liquidationBase1 }, + } + ) }, async ({ comet, actors }, _context, world) => { const { albert, betty } = actors; @@ -583,7 +588,7 @@ scenario( const [initialNumAbsorbs, initialNumAbsorbed] = await comet.liquidatorPoints(betty.address); const borrowCapacity = await borrowCapacityForAsset(comet, albert, 0); - const borrowAmount = (borrowCapacity.mul(90n)).div(100n); + const borrowAmount = (borrowCapacity.mul(getConfigForScenario(_context).liquidationDenominator)).div(100n); await albert.withdrawAsset({ asset: baseToken, @@ -648,16 +653,20 @@ scenario( scenario( `LiquidationBot > absorbs, but does not attempt to purchase collateral when maxAmountToPurchase=0`, { - filter: async (ctx) => matchesDeployment(ctx, [{ network: 'mainnet' }, { network: 'polygon' }, { network: 'arbitrum' }]) && !matchesDeployment(ctx, [{deployment: 'wsteth', network: 'mainnet'}]), - tokenBalances: { - $comet: { $base: 100000 }, - }, - cometBalances: { - albert: { - $asset0: ' == 200', - }, - betty: { $base: 1000 }, - }, + filter: async (ctx) => matchesDeployment(ctx, [{ network: 'mainnet' }, { network: 'polygon' }, { network: 'arbitrum' }]), + tokenBalances: async (ctx) => ( + { + $comet: { $base: getConfigForScenario(ctx).liquidationBase }, + } + ), + cometBalances: async (ctx) => ( + { + albert: { + $asset0: ` == ${getConfigForScenario(ctx).liquidationAsset}}`, + }, + betty: { $base: getConfigForScenario(ctx).liquidationBase1 }, + } + ) }, async ({ comet, actors }, _context, world) => { const { albert, betty } = actors; @@ -694,7 +703,7 @@ scenario( const [initialNumAbsorbs, initialNumAbsorbed] = await comet.liquidatorPoints(betty.address); const borrowCapacity = await borrowCapacityForAsset(comet, albert, 0); - const borrowAmount = (borrowCapacity.mul(90n)).div(100n); + const borrowAmount = (borrowCapacity.mul(getConfigForScenario(_context).liquidationDenominator)).div(100n); await albert.withdrawAsset({ asset: baseToken, @@ -761,7 +770,8 @@ scenario( mainnet: { usdc: 2250000, weth: 20, - usdt: 2250000 + usdt: 2250000, + usds: 225000, }, }; const assetAmounts = { @@ -769,6 +779,7 @@ scenario( usdc: ' == 5000', // COMP weth: ' == 7000', // CB_ETH usdt: ' == 5000', // COMP + usds: ' == 850', // WETH }, }; @@ -778,7 +789,7 @@ scenario( upgrade: { targetReserves: exp(20_000, 18) }, - filter: async (ctx) => matchesDeployment(ctx, [{ network: 'mainnet' }]) && !matchesDeployment(ctx, [{deployment: 'wsteth'}]), + filter: async (ctx) => matchesDeployment(ctx, [{ network: 'mainnet' }]) && !matchesDeployment(ctx, [{deployment: 'wsteth'}, {deployment: 'usds'}]), tokenBalances: async (ctx) => ( { $comet: { @@ -829,7 +840,7 @@ scenario( const [initialNumAbsorbs, initialNumAbsorbed] = await comet.liquidatorPoints(betty.address); const borrowCapacity = await borrowCapacityForAsset(comet, albert, 0); - const borrowAmount = (borrowCapacity.mul(90n)).div(100n); + const borrowAmount = (borrowCapacity.mul(getConfigForScenario(_context).liquidationDenominator)).div(100n); await albert.withdrawAsset({ asset: baseToken, diff --git a/scenario/MainnetBulkerScenario.ts b/scenario/MainnetBulkerScenario.ts index 997600cb2..fdfa20a5e 100644 --- a/scenario/MainnetBulkerScenario.ts +++ b/scenario/MainnetBulkerScenario.ts @@ -52,9 +52,9 @@ scenario( const toSupplyStEth = exp(.1, 18); - await context.sourceTokens(toSupplyStEth, new CometAsset(stETH), albert); + await context.sourceTokens(toSupplyStEth + 3n, new CometAsset(stETH), albert); - expect(await stETH.balanceOf(albert.address)).to.be.approximately(toSupplyStEth, 2); + expect(await stETH.balanceOf(albert.address)).to.be.greaterThanOrEqual(toSupplyStEth); // approve bulker as albert await stETH.connect(albert.signer).approve(bulker.address, toSupplyStEth); @@ -68,7 +68,7 @@ scenario( await albert.invoke({ actions, calldata }); - expect(await stETH.balanceOf(albert.address)).to.be.equal(0n); + expectApproximately((await stETH.balanceOf(albert.address)).toBigInt(), 0n, 2n); expectApproximately( (await comet.collateralBalanceOf(albert.address, wstETH.address)).toBigInt(), (await wstETH.getWstETHByStETH(toSupplyStEth)).toBigInt(), diff --git a/scenario/TransferScenario.ts b/scenario/TransferScenario.ts index 06d7151c0..796446453 100644 --- a/scenario/TransferScenario.ts +++ b/scenario/TransferScenario.ts @@ -2,6 +2,7 @@ import { CometContext, scenario } from './context/CometContext'; import { expect } from 'chai'; import { expectApproximately, expectBase, expectRevertCustom, getInterest, hasMinBorrowGreaterThanOne, isTriviallySourceable, isValidAssetIndex, MAX_ASSETS } from './utils'; import { ContractReceipt } from 'ethers'; +import { getConfigForScenario } from './utils/scenarioHelper'; async function testTransferCollateral(context: CometContext, assetNum: number): Promise { const comet = await context.getComet(); @@ -181,8 +182,8 @@ scenario( const borrowRate = (await comet.getBorrowRate(utilization)).toBigInt(); // XXX 70 seconds?! - expectApproximately(await albert.getCometBaseBalance(), 1000n * scale, getInterest(1000n * scale, borrowRate, 70n) + 2n); - expectApproximately(await betty.getCometBaseBalance(), -1000n * scale, getInterest(1000n * scale, borrowRate, 70n) + 2n); + expectApproximately(await albert.getCometBaseBalance(), 1000n * scale, getInterest(1000n * scale, borrowRate, BigInt(getConfigForScenario(context).interestSeconds)) + 2n); + expectApproximately(await betty.getCometBaseBalance(), -1000n * scale, getInterest(1000n * scale, borrowRate, BigInt(getConfigForScenario(context).interestSeconds)) + 2n); await albert.allow(betty, true); @@ -190,8 +191,8 @@ scenario( const toTransfer = 999n * scale; // XXX cannot withdraw 1000 (to ~0) const txn = await betty.transferAssetFrom({ src: albert.address, dst: betty.address, asset: baseAsset.address, amount: toTransfer }); - expectApproximately(await albert.getCometBaseBalance(), scale, getInterest(1000n * scale, borrowRate, 70n) + 2n); - expectApproximately(await betty.getCometBaseBalance(), -scale, getInterest(1000n * scale, borrowRate, 70n) + 2n); + expectApproximately(await albert.getCometBaseBalance(), scale, getInterest(1000n * scale, borrowRate, BigInt(getConfigForScenario(context).interestSeconds)) + 2n); + expectApproximately(await betty.getCometBaseBalance(), -scale, getInterest(1000n * scale, borrowRate, BigInt(getConfigForScenario(context).interestSeconds)) + 2n); return txn; // return txn to measure gas } @@ -249,8 +250,8 @@ scenario( const borrowRate = (await comet.getBorrowRate(utilization)).toBigInt(); // XXX 70 seconds?! - expectApproximately(await albert.getCometBaseBalance(), 1000n * scale, getInterest(1000n * scale, borrowRate, 70n) + 2n); - expectApproximately(await betty.getCometBaseBalance(), -1000n * scale, getInterest(1000n * scale, borrowRate, 70n) + 2n); + expectApproximately(await albert.getCometBaseBalance(), 1000n * scale, getInterest(1000n * scale, borrowRate, BigInt(getConfigForScenario(context).interestSeconds)) + 2n); + expectApproximately(await betty.getCometBaseBalance(), -1000n * scale, getInterest(1000n * scale, borrowRate, BigInt(getConfigForScenario(context).interestSeconds)) + 2n); await albert.allow(betty, true); diff --git a/scenario/constraints/NativeTokenConstraint.ts b/scenario/constraints/NativeTokenConstraint.ts index b3eed1869..21b1dcbca 100644 --- a/scenario/constraints/NativeTokenConstraint.ts +++ b/scenario/constraints/NativeTokenConstraint.ts @@ -8,7 +8,7 @@ export class NativeTokenConstraint implements StaticCons async function (ctx: T): Promise { for (const symbol in ctx.assets) { const contract = await ctx.world.deploymentManager.contract(symbol); - if (contract && contract['deposit()']) { + if (contract && contract['deposit()'] && contract['withdraw(uint256)']) { const [whale]= await ctx.getWhales(); if (!whale) { throw new Error(`NativeTokenConstraint: no whale found for ${ctx.world.deploymentManager.network}`); diff --git a/scenario/constraints/ProposalConstraint.ts b/scenario/constraints/ProposalConstraint.ts index 4c0b970d1..353c1c61f 100644 --- a/scenario/constraints/ProposalConstraint.ts +++ b/scenario/constraints/ProposalConstraint.ts @@ -62,9 +62,9 @@ export class ProposalConstraint implements StaticConstra ); } - // temporary hack to skip proposal 329 - if (proposal.id.eq(329)) { - console.log('Skipping proposal 329'); + // temporary hack to skip proposal 349 + if (proposal.id.eq(349)) { + console.log('Skipping proposal 349'); continue; } diff --git a/scenario/utils/scenarioHelper.ts b/scenario/utils/scenarioHelper.ts new file mode 100644 index 000000000..5d63f4599 --- /dev/null +++ b/scenario/utils/scenarioHelper.ts @@ -0,0 +1,54 @@ +import { CometContext } from '../context/CometContext'; + +const config = { + bulkerBase: 1000000, + bulkerAsset: 5000, + bulkerComet: 5000, + bulkerBorrowBase: 1000, + bulkerBorrowAsset: 500, + liquidationBase: 100000, + liquidationBase1: 1000, + liquidationAsset: 200, + liquidationDenominator: 90, + rewardsAsset: 10000, + rewardsBase: 1000, + transferBase: 1000, + transferAsset: 5000, + interestSeconds: 110 +}; + +export function getConfigForScenario(ctx: CometContext) { + if (ctx.world.base.network === 'mainnet' && ctx.world.base.deployment === 'wbtc') { + config.bulkerBase = 5000; + config.bulkerAsset = 200; + config.bulkerComet = 200; + config.bulkerBorrowBase = 100; + config.bulkerBorrowAsset = 50; + config.liquidationBase = 1000; + config.liquidationBase1 = 500; + config.liquidationAsset = 100; + config.rewardsAsset = 1000; + config.rewardsBase = 100; + config.transferBase = 100; + config.transferAsset = 500; + config.interestSeconds = 70; + } + + if (ctx.world.base.network === 'mainnet' && ctx.world.base.deployment === 'wsteth') { + config.liquidationBase = 10000; + config.liquidationBase1 = 1000; + config.liquidationAsset = 100; + config.liquidationDenominator = 84; + config.interestSeconds = 70; + } + + if (ctx.world.base.network === 'mainnet' && ctx.world.base.deployment === 'usds') { + config.liquidationAsset = 100; + } + + if (ctx.world.base.network === 'base' && ctx.world.base.deployment === 'aero') { + config.interestSeconds = 110; + } + + return config; +} \ No newline at end of file diff --git a/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts b/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts index b2d5d4e0b..29a70303e 100644 --- a/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts +++ b/scripts/liquidation_bot/liquidateUnderwaterBorrowers.ts @@ -39,6 +39,7 @@ const addresses = { WETH9: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', CB_ETH: '0xBe9895146f7AF43049ca1c1AE358B0541Ea49704', WST_ETH: '0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0', + RS_ETH: '0xA1290d69c65A6Fe4DF752f95823fae25cB99e5A7', USDT: '0xdAC17F958D2ee523a2206206994597C13D831ec7' }, goerli: { @@ -94,6 +95,14 @@ export const flashLoanPools = { usdt: { tokenAddress: addresses.mainnet.DAI, poolFee: 100 + }, + wsteth: { + tokenAddress: addresses.mainnet.WETH9, + poolFee: 100 + }, + usds: { + tokenAddress: addresses.mainnet.DAI, + poolFee: 3000, } }, goerli: { @@ -189,6 +198,13 @@ export function getPoolConfig(tokenAddress: string) { uniswapPoolFee: 500 } }, + [addresses.mainnet.RS_ETH.toLowerCase()]: { + ...defaultPoolConfig, + ...{ + exchange: Exchange.Balancer, + balancerPoolId: '0x58aadfb1afac0ad7fca1148f3cde6aedf5236b6d00000000000000000000067f' + } + }, [addresses.mainnet.WST_ETH.toLowerCase()]: { ...defaultPoolConfig, ...{ diff --git a/src/deploy/index.ts b/src/deploy/index.ts index 7381208a9..8719843f9 100644 --- a/src/deploy/index.ts +++ b/src/deploy/index.ts @@ -89,6 +89,8 @@ export const WHALES = { '0x2775b1c75658be0f640272ccb8c72ac986009e38', '0x1a9c8182c09f50c8318d769245bea52c32be35bc', '0x3c22ec75ea5D745c78fc84762F7F1E6D82a2c5BF', + '0x88a1493366D48225fc3cEFbdae9eBb23E323Ade3', // USDe whale + '0x43594da5d6A03b2137a04DF5685805C676dEf7cB', // rsETH whale '0x3d9819210a31b4961b30ef54be2aed79b9c9cd3b' ], polygon: [