From 2992f8efcbaf5edfa1a3e283fd0709c4902843d5 Mon Sep 17 00:00:00 2001 From: MSamiTariq Date: Wed, 23 Oct 2024 17:11:29 +0500 Subject: [PATCH 1/2] Fix network issues --- hardhat.config.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index d3ced2a..bdf9443 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -124,7 +124,7 @@ const networkConfigs: NetworkConfig[] = [ { network: 'base', chainId: 8453, - url: `https://fluent-prettiest-scion.base-mainnet.quiknode.pro/${QUICKNODE_KEY}`, + url: `https://base-mainnet.infura.io/v3/${INFURA_KEY}`, }, { network: 'arbitrum', @@ -262,6 +262,20 @@ const config: HardhatUserConfig = { berlin: 0, shanghai: 0, } + }, + 137: { + hardforkHistory: { + london: 0, + berlin: 0, + shanghai: 0, + } + }, + 8453: { + hardforkHistory: { + london: 0, + berlin: 0, + shanghai: 0, + } } } }, From 5a993f93f99486cd14c3136d55b3bdfa3cd0c735 Mon Sep 17 00:00:00 2001 From: MSamiTariq Date: Wed, 23 Oct 2024 22:21:50 +0500 Subject: [PATCH 2/2] added migrations for arbitrum, base, fuji, mainnet, polygon, and sepolia --- .../1729693349_gov_market_updates.ts | 241 ++++++++++++++++++ .../1729694613_gov_market_updates.ts | 209 +++++++++++++++ .../1729695741_gov_market_updates.ts | 159 ++++++++++++ .../1729696345_gov_market_updates.ts | 176 +++++++++++++ .../1729698710_gov_market_updates.ts | 200 +++++++++++++++ .../1729700340_gov_market_updates.ts | 164 ++++++++++++ 6 files changed, 1149 insertions(+) create mode 100644 deployments/arbitrum/usdc/migrations/1729693349_gov_market_updates.ts create mode 100644 deployments/base/usdc/migrations/1729694613_gov_market_updates.ts create mode 100644 deployments/fuji/usdc/migrations/1729695741_gov_market_updates.ts create mode 100644 deployments/mainnet/usdc/migrations/1729696345_gov_market_updates.ts create mode 100644 deployments/polygon/usdc/migrations/1729698710_gov_market_updates.ts create mode 100644 deployments/sepolia/usdc/migrations/1729700340_gov_market_updates.ts diff --git a/deployments/arbitrum/usdc/migrations/1729693349_gov_market_updates.ts b/deployments/arbitrum/usdc/migrations/1729693349_gov_market_updates.ts new file mode 100644 index 0000000..b9f2bfd --- /dev/null +++ b/deployments/arbitrum/usdc/migrations/1729693349_gov_market_updates.ts @@ -0,0 +1,241 @@ +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { + MarketAdminPermissionChecker, + MarketUpdateProposer, + MarketUpdateTimelock, + CometProxyAdmin, + Configurator, +} from '../../../../build/types'; +import { applyL1ToL2Alias, estimateL2Transaction } from '../../../../scenario/utils/arbitrumUtils'; +import { expect } from 'chai'; +import { proposal } from '../../../../src/deploy'; + +interface Vars {} + +const marketAdminAddress = '0x7e14050080306cd36b47DE61ce604b3a1EC70c4e'; + +const localTimelockAddress = '0x3fB4d38ea7EC20D91917c09591490Eeda38Cf88A'; + +const marketUpdateTimelockAddress = '0x81Bc6016Fa365bfE929a51Eec9217B441B598eC6'; +const marketUpdateProposerAddress = '0xB6Ef3AC71E9baCF1F4b9426C149d855Bfc4415F9'; +const newConfiguratorImplementationAddress = '0x371DB45c7ee248dAFf4Dc1FFB67A20faa0ecFE02'; +const newCometProxyAdminAddress = '0x24D86Da09C4Dd64e50dB7501b0f695d030f397aF'; +const marketAdminPermissionCheckerAddress = '0x62DD0452411113404cf9a7fE88A5E6E86f9B71a6'; + +const communityMultiSigAddress = '0x78E6317DD6D43DdbDa00Dce32C2CbaFc99361a9d'; + +const cometProxyAdminOldAddress = '0xD10b40fF1D92e2267D099Da3509253D9Da4D715e'; +const configuratorProxyAddress = '0xb21b06D71c75973babdE35b49fFDAc3F82Ad3775'; +const cometProxyUsdcAddress = '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf'; +const cometProxyUsdtAddress = '0xd98Be00b5D27fc98112BdE293e487f8D4cA57d07'; +const cometProxyWethAddress = '0x6f7D514bbD4aFf3BcD1140B7344b32f063dEe486'; +const cometProxyUsdceAddress = '0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA'; + +export default migration('1729693349_gov_market_updates', { + prepare: async () => { + return {}; + }, + + enact: async ( + deploymentManager: DeploymentManager, + govDeploymentManager: DeploymentManager, + ) => { + const trace = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + const { utils } = ethers; + + const { bridgeReceiver, timelock: l2Timelock } = + await deploymentManager.getContracts(); + + const { arbitrumInbox, governor, timelock } = + await govDeploymentManager.getContracts(); + + + const changeProxyAdminForCometProxyUsdcCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [cometProxyUsdcAddress, newCometProxyAdminAddress] + ); + + const changeProxyAdminForCometProxyUsdtCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [cometProxyUsdtAddress, newCometProxyAdminAddress] + ); + + const changeProxyAdminForCometProxyWethCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [cometProxyWethAddress, newCometProxyAdminAddress] + ); + + const changeProxyAdminForCometProxyUsdceCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [cometProxyUsdceAddress, newCometProxyAdminAddress] + ); + + const changeProxyAdminForConfiguratorProxyCalldata = + utils.defaultAbiCoder.encode( + ['address', 'address'], + [configuratorProxyAddress, newCometProxyAdminAddress] + ); + + const upgradeConfiguratorProxyCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [configuratorProxyAddress, newConfiguratorImplementationAddress] + ); + + const setMarketAdminPermissionCheckerForConfiguratorProxyCalldata = + utils.defaultAbiCoder.encode( + ['address'], + [marketAdminPermissionCheckerAddress] + ); + + const l2ProposalData = utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'string[]', 'bytes[]'], + [ + [ + cometProxyAdminOldAddress, + cometProxyAdminOldAddress, + cometProxyAdminOldAddress, + cometProxyAdminOldAddress, + cometProxyAdminOldAddress, + newCometProxyAdminAddress, + configuratorProxyAddress, + ], + [0, 0, 0, 0, 0, 0, 0], + [ + 'changeProxyAdmin(address,address)', + 'changeProxyAdmin(address,address)', + 'changeProxyAdmin(address,address)', + 'changeProxyAdmin(address,address)', + 'changeProxyAdmin(address,address)', + 'upgrade(address,address)', + 'setMarketAdminPermissionChecker(address)', + ], + [ + changeProxyAdminForCometProxyUsdcCalldata, + changeProxyAdminForCometProxyUsdtCalldata, + changeProxyAdminForCometProxyWethCalldata, + changeProxyAdminForCometProxyUsdceCalldata, + changeProxyAdminForConfiguratorProxyCalldata, + upgradeConfiguratorProxyCalldata, + setMarketAdminPermissionCheckerForConfiguratorProxyCalldata, + ], + ] + ); + + const refundAddress = l2Timelock.address; + + const createRetryableTicketGasParams = await estimateL2Transaction( + { + from: applyL1ToL2Alias(timelock.address), + to: bridgeReceiver.address, + data: l2ProposalData, + }, + deploymentManager + ); + + const mainnetActions = [ + { + contract: arbitrumInbox, + signature: 'createRetryableTicket(address,uint256,uint256,address,address,uint256,uint256,bytes)', + args: [ + bridgeReceiver.address, // address to, + 0, // uint256 l2CallValue, + createRetryableTicketGasParams.maxSubmissionCost, // uint256 maxSubmissionCost, + refundAddress, // address excessFeeRefundAddress, + refundAddress, // address callValueRefundAddress, + createRetryableTicketGasParams.gasLimit, // uint256 gasLimit, + createRetryableTicketGasParams.maxFeePerGas, // uint256 maxFeePerGas, + l2ProposalData, // bytes calldata data + ], + value: createRetryableTicketGasParams.deposit + } + ]; + + const description = + `DoDAO has been examining various areas where improvements can be made to governance in Compound through tooling or automation. A significant issue raised repeatedly by many members concerns the time and effort required to apply Market Updates. + +Currently, approximately 70-90% of the proposals pertain solely to updating market parameters. Gauntlet uses analytics and algorithms to determine new parameters based on current market conditions. These proposals are highly specific and require unique skills for validation. So far, we have seen minimal participation from other community members or teams in validating these parameters. + +Assuming the total cost of reviewing a proposal (including the effort of all delegates) is $2,000 per proposal, we are spending upwards of $300,000 per year on a process that currently acts more as a friction layer without much safeguard, as these market update proposals are rarely reviewed thoroughly. + +This not only slows down the process but also diverts focus from reviewing essential proposals related to new partnerships, the addition of new chains, and the introduction of assets, etc. + +We propose a parallel process specifically for market updates that can bypass the normal governance lifecycle. This would enable us, as a Compound community, to move faster and concentrate on the most critical decisions. + +This proposal has already been discussed here - https://www.comp.xyz/t/market-updates-alternate-governance-track/5379 + +The forum post includes two solutions, and OpenZeppelin has provided details and feedback on those solutions. + +After discussing with OpenZeppelin, DoDAO and OZ together believe that given the amount of changes, updating the Configurator could be the best solution. OpenZeppelin mentioned a couple of important points in the forum post: + +1. Grant the market admin role to an Safe address, which can be maintained by Gauntlet or other community members. +2. Market Updates to the Configurator will go through a timelock, providing sufficient time for the community to review or even block the market updates via this alternate route. +`; + + 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 false; + }, + + async verify(deploymentManager: DeploymentManager) { + await deploymentManager.spider(); + const tracer = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + + const { configurator } = await deploymentManager.getContracts(); + + const marketAdminPermissionChecker = (await ethers.getContractAt( + 'MarketAdminPermissionChecker', + marketAdminPermissionCheckerAddress + )) as MarketAdminPermissionChecker; + + const marketUpdateTimelock = (await ethers.getContractAt( + 'MarketUpdateTimelock', + marketUpdateTimelockAddress + )) as MarketUpdateTimelock; + + const marketUpdateProposer = (await ethers.getContractAt( + 'MarketUpdateProposer', + marketUpdateProposerAddress + )) as MarketUpdateProposer; + + const cometProxyAdminNew = (await ethers.getContractAt( + 'CometProxyAdmin', + newCometProxyAdminAddress + )) as CometProxyAdmin; + + expect(configurator.address).to.be.equal(configuratorProxyAddress); + expect(await (configurator as Configurator).governor()).to.be.equal(localTimelockAddress); + expect(await (configurator as Configurator).marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionCheckerAddress); + + expect(await cometProxyAdminNew.marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionChecker.address); + expect(await cometProxyAdminNew.owner()).to.be.equal(localTimelockAddress); + + expect(await marketAdminPermissionChecker.marketAdmin()).to.be.equal(marketUpdateTimelockAddress); + expect(await marketAdminPermissionChecker.owner()).to.be.equal(localTimelockAddress); + expect(await marketAdminPermissionChecker.marketAdminPauseGuardian()).to.be.equal(communityMultiSigAddress); + + expect(await marketUpdateTimelock.marketUpdateProposer()).to.be.equal(marketUpdateProposer.address); + expect(await marketUpdateTimelock.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateTimelock.delay()).to.be.equal(2 * 24 * 60 * 60); + + expect(await marketUpdateProposer.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateProposer.marketAdmin()).to.be.equal(marketAdminAddress); + + expect(await marketUpdateProposer.timelock()).to.be.equal(marketUpdateTimelock.address); + expect(await marketUpdateProposer.proposalGuardian()).to.be.equal(communityMultiSigAddress); + + tracer('All checks passed.'); + }, +}); diff --git a/deployments/base/usdc/migrations/1729694613_gov_market_updates.ts b/deployments/base/usdc/migrations/1729694613_gov_market_updates.ts new file mode 100644 index 0000000..bf95c48 --- /dev/null +++ b/deployments/base/usdc/migrations/1729694613_gov_market_updates.ts @@ -0,0 +1,209 @@ +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { + MarketAdminPermissionChecker, + MarketUpdateProposer, + MarketUpdateTimelock, + CometProxyAdmin, + Configurator, +} from './../../../../build/types'; +import { expect } from 'chai'; +import { proposal } from '../../../../src/deploy'; + +interface Vars {} + +const marketAdminAddress = '0x7e14050080306cd36b47DE61ce604b3a1EC70c4e'; + +const localTimelockAddress = '0xCC3E7c85Bb0EE4f09380e041fee95a0caeDD4a02'; + +const marketUpdateTimelockAddress = '0x81Bc6016Fa365bfE929a51Eec9217B441B598eC6'; +const marketUpdateProposerAddress = '0xB6Ef3AC71E9baCF1F4b9426C149d855Bfc4415F9'; +const newConfiguratorImplementationAddress = '0x371DB45c7ee248dAFf4Dc1FFB67A20faa0ecFE02'; +const newCometProxyAdminAddress = '0x24D86Da09C4Dd64e50dB7501b0f695d030f397aF'; +const marketAdminPermissionCheckerAddress = '0x62DD0452411113404cf9a7fE88A5E6E86f9B71a6'; + +const communityMultiSigAddress = '0x3cb4653F3B45F448D9100b118B75a1503281d2ee'; + +const cometProxyAdminOldAddress = '0xbdE8F31D2DdDA895264e27DD990faB3DC87b372d'; +const configuratorProxyAddress = '0x45939657d1CA34A8FA39A924B71D28Fe8431e581'; +const cometProxyUsdcAddress = '0xb125E6687d4313864e53df431d5425969c15Eb2F'; +const cometProxyUsdbcAddress = '0x9c4ec768c28520B50860ea7a15bd7213a9fF58bf'; +const cometProxyWethAddress = '0x46e6b214b524310239732D51387075E0e70970bf'; + +export default migration('1729694613_gov_market_updates', { + prepare: async () => { + return {}; + }, + + enact: async ( + deploymentManager: DeploymentManager, + govDeploymentManager: DeploymentManager, + ) => { + const trace = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + const { utils } = ethers; + + const { bridgeReceiver } = await deploymentManager.getContracts(); + + const { baseL1CrossDomainMessenger, governor } = + await govDeploymentManager.getContracts(); + + + const changeProxyAdminForCometProxyUsdcCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [cometProxyUsdcAddress, newCometProxyAdminAddress] + ); + + const changeProxyAdminForCometProxyUsdbcCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [cometProxyUsdbcAddress, newCometProxyAdminAddress] + ); + + const changeProxyAdminForCometProxyWethCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [cometProxyWethAddress, newCometProxyAdminAddress] + ); + + const changeProxyAdminForConfiguratorProxyCalldata = + utils.defaultAbiCoder.encode( + ['address', 'address'], + [configuratorProxyAddress, newCometProxyAdminAddress] + ); + + const upgradeConfiguratorProxyCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [configuratorProxyAddress, newConfiguratorImplementationAddress] + ); + + const setMarketAdminPermissionCheckerForConfiguratorProxyCalldata = + utils.defaultAbiCoder.encode( + ['address'], + [marketAdminPermissionCheckerAddress] + ); + + const l2ProposalData = utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'string[]', 'bytes[]'], + [ + [ + cometProxyAdminOldAddress, + cometProxyAdminOldAddress, + cometProxyAdminOldAddress, + cometProxyAdminOldAddress, + newCometProxyAdminAddress, + configuratorProxyAddress, + ], + [0, 0, 0, 0, 0, 0], + [ + 'changeProxyAdmin(address,address)', + 'changeProxyAdmin(address,address)', + 'changeProxyAdmin(address,address)', + 'changeProxyAdmin(address,address)', + 'upgrade(address,address)', + 'setMarketAdminPermissionChecker(address)', + ], + [ + changeProxyAdminForCometProxyUsdcCalldata, + changeProxyAdminForCometProxyUsdbcCalldata, + changeProxyAdminForCometProxyWethCalldata, + changeProxyAdminForConfiguratorProxyCalldata, + upgradeConfiguratorProxyCalldata, + setMarketAdminPermissionCheckerForConfiguratorProxyCalldata, + ], + ] + ); + + const actions = [ + { + contract: baseL1CrossDomainMessenger, + signature: 'sendMessage(address,bytes,uint32)', + args: [bridgeReceiver.address, l2ProposalData, 2_500_000] + } + ]; + + const description = + `DoDAO has been examining various areas where improvements can be made to governance in Compound through tooling or automation. A significant issue raised repeatedly by many members concerns the time and effort required to apply Market Updates. + +Currently, approximately 70-90% of the proposals pertain solely to updating market parameters. Gauntlet uses analytics and algorithms to determine new parameters based on current market conditions. These proposals are highly specific and require unique skills for validation. So far, we have seen minimal participation from other community members or teams in validating these parameters. + +Assuming the total cost of reviewing a proposal (including the effort of all delegates) is $2,000 per proposal, we are spending upwards of $300,000 per year on a process that currently acts more as a friction layer without much safeguard, as these market update proposals are rarely reviewed thoroughly. + +This not only slows down the process but also diverts focus from reviewing essential proposals related to new partnerships, the addition of new chains, and the introduction of assets, etc. + +We propose a parallel process specifically for market updates that can bypass the normal governance lifecycle. This would enable us, as a Compound community, to move faster and concentrate on the most critical decisions. + +This proposal has already been discussed here - https://www.comp.xyz/t/market-updates-alternate-governance-track/5379 + +The forum post includes two solutions, and OpenZeppelin has provided details and feedback on those solutions. + +After discussing with OpenZeppelin, DoDAO and OZ together believe that given the amount of changes, updating the Configurator could be the best solution. OpenZeppelin mentioned a couple of important points in the forum post: + +1. Grant the market admin role to an Safe address, which can be maintained by Gauntlet or other community members. +2. Market Updates to the Configurator will go through a timelock, providing sufficient time for the community to review or even block the market updates via this alternate route. +`; + + 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(deploymentManager: DeploymentManager): Promise { + return false; + }, + + async verify(deploymentManager: DeploymentManager) { + await deploymentManager.spider(); + const tracer = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + + const { configurator } = await deploymentManager.getContracts(); + + const marketAdminPermissionChecker = (await ethers.getContractAt( + 'MarketAdminPermissionChecker', + marketAdminPermissionCheckerAddress + )) as MarketAdminPermissionChecker; + + const marketUpdateTimelock = (await ethers.getContractAt( + 'MarketUpdateTimelock', + marketUpdateTimelockAddress + )) as MarketUpdateTimelock; + + const marketUpdateProposer = (await ethers.getContractAt( + 'MarketUpdateProposer', + marketUpdateProposerAddress + )) as MarketUpdateProposer; + + const cometProxyAdminNew = (await ethers.getContractAt( + 'CometProxyAdmin', + newCometProxyAdminAddress + )) as CometProxyAdmin; + + expect(configurator.address).to.be.equal(configuratorProxyAddress); + expect(await (configurator as Configurator).governor()).to.be.equal(localTimelockAddress); + expect(await (configurator as Configurator).marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionCheckerAddress); + + expect(await cometProxyAdminNew.marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionChecker.address); + expect(await cometProxyAdminNew.owner()).to.be.equal(localTimelockAddress); + + expect(await marketAdminPermissionChecker.marketAdmin()).to.be.equal(marketUpdateTimelockAddress); + expect(await marketAdminPermissionChecker.owner()).to.be.equal(localTimelockAddress); + expect(await marketAdminPermissionChecker.marketAdminPauseGuardian()).to.be.equal(communityMultiSigAddress); + + expect(await marketUpdateTimelock.marketUpdateProposer()).to.be.equal(marketUpdateProposer.address); + expect(await marketUpdateTimelock.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateTimelock.delay()).to.be.equal(2 * 24 * 60 * 60); + + expect(await marketUpdateProposer.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateProposer.marketAdmin()).to.be.equal(marketAdminAddress); + + expect(await marketUpdateProposer.timelock()).to.be.equal(marketUpdateTimelock.address); + expect(await marketUpdateProposer.proposalGuardian()).to.be.equal(communityMultiSigAddress); + + tracer('All checks passed.'); + }, +}); diff --git a/deployments/fuji/usdc/migrations/1729695741_gov_market_updates.ts b/deployments/fuji/usdc/migrations/1729695741_gov_market_updates.ts new file mode 100644 index 0000000..e4f8c41 --- /dev/null +++ b/deployments/fuji/usdc/migrations/1729695741_gov_market_updates.ts @@ -0,0 +1,159 @@ +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { + MarketAdminPermissionChecker, + MarketUpdateProposer, + MarketUpdateTimelock, + CometProxyAdmin, + Configurator, +} from './../../../../build/types'; +import { expect } from 'chai'; +import { proposal } from '../../../../src/deploy'; +import { Contract } from 'ethers'; + +interface Vars {} + +const marketAdminAddress = ''; // Don't currently have this address + +const localTimelockAddress = ''; // Don't currently have this address + +const marketUpdateTimelockAddress = '0x81Bc6016Fa365bfE929a51Eec9217B441B598eC6'; +const marketUpdateProposerAddress = '0xB6Ef3AC71E9baCF1F4b9426C149d855Bfc4415F9'; +const newConfiguratorImplementationAddress = '0x371DB45c7ee248dAFf4Dc1FFB67A20faa0ecFE02'; +const newCometProxyAdminAddress = '0x24D86Da09C4Dd64e50dB7501b0f695d030f397aF'; +const marketAdminPermissionCheckerAddress = '0x62DD0452411113404cf9a7fE88A5E6E86f9B71a6'; + +const communityMultiSigAddress = ''; // Don't currently have this address + +const configuratorProxyAddress = '0x8c083632099CBA949EA61A3044DB1B5A27818b20'; +const cometProxyUsdcAddress = '0x59BF4753899C20EA152dEefc6f6A14B2a5CC3021'; + +export default migration('1729695741_gov_market_updates', { + prepare: async () => { + return {}; + }, + + enact: async ( + deploymentManager: DeploymentManager, + govDeploymentManager: DeploymentManager, + ) => { + const trace = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + + const { governor, cometAdmin, configurator } = await deploymentManager.getContracts(); + + const newCometProxyAdmin = (await ethers.getContractAt( + 'CometProxyAdmin', + newCometProxyAdminAddress + )) as Contract; + + const actions = [ + { + contract: cometAdmin, + signature: 'changeProxyAdmin(address,address)', + args: [cometProxyUsdcAddress, newCometProxyAdminAddress], + }, + { + contract: cometAdmin, + signature: 'changeProxyAdmin(address,address)', + args: [configuratorProxyAddress, newCometProxyAdminAddress], + }, + { + contract: newCometProxyAdmin, + signature: 'upgrade(address,address)', + args: [configuratorProxyAddress, newConfiguratorImplementationAddress], + }, + { + contract: configurator, + signature: 'setMarketAdminPermissionChecker(address)', + args: [marketAdminPermissionCheckerAddress], + }, + ]; + + const description = + `DoDAO has been examining various areas where improvements can be made to governance in Compound through tooling or automation. A significant issue raised repeatedly by many members concerns the time and effort required to apply Market Updates. + +Currently, approximately 70-90% of the proposals pertain solely to updating market parameters. Gauntlet uses analytics and algorithms to determine new parameters based on current market conditions. These proposals are highly specific and require unique skills for validation. So far, we have seen minimal participation from other community members or teams in validating these parameters. + +Assuming the total cost of reviewing a proposal (including the effort of all delegates) is $2,000 per proposal, we are spending upwards of $300,000 per year on a process that currently acts more as a friction layer without much safeguard, as these market update proposals are rarely reviewed thoroughly. + +This not only slows down the process but also diverts focus from reviewing essential proposals related to new partnerships, the addition of new chains, and the introduction of assets, etc. + +We propose a parallel process specifically for market updates that can bypass the normal governance lifecycle. This would enable us, as a Compound community, to move faster and concentrate on the most critical decisions. + +This proposal has already been discussed here - https://www.comp.xyz/t/market-updates-alternate-governance-track/5379 + +The forum post includes two solutions, and OpenZeppelin has provided details and feedback on those solutions. + +After discussing with OpenZeppelin, DoDAO and OZ together believe that given the amount of changes, updating the Configurator could be the best solution. OpenZeppelin mentioned a couple of important points in the forum post: + +1. Grant the market admin role to an Safe address, which can be maintained by Gauntlet or other community members. +2. Market Updates to the Configurator will go through a timelock, providing sufficient time for the community to review or even block the market updates via this alternate route. +`; + + 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(deploymentManager: DeploymentManager): Promise { + return false; + }, + + async verify(deploymentManager: DeploymentManager) { + await deploymentManager.spider(); + const tracer = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + + const { configurator } = await deploymentManager.getContracts(); + + const marketAdminPermissionChecker = (await ethers.getContractAt( + 'MarketAdminPermissionChecker', + marketAdminPermissionCheckerAddress + )) as MarketAdminPermissionChecker; + + const marketUpdateTimelock = (await ethers.getContractAt( + 'MarketUpdateTimelock', + marketUpdateTimelockAddress + )) as MarketUpdateTimelock; + + const marketUpdateProposer = (await ethers.getContractAt( + 'MarketUpdateProposer', + marketUpdateProposerAddress + )) as MarketUpdateProposer; + + const cometProxyAdminNew = (await ethers.getContractAt( + 'CometProxyAdmin', + newCometProxyAdminAddress + )) as CometProxyAdmin; + + expect(configurator.address).to.be.equal(configuratorProxyAddress); + expect(await (configurator as Configurator).governor()).to.be.equal(localTimelockAddress); + expect(await (configurator as Configurator).marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionCheckerAddress); + + expect(await cometProxyAdminNew.marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionChecker.address); + expect(await cometProxyAdminNew.owner()).to.be.equal(localTimelockAddress); + + expect(await marketAdminPermissionChecker.marketAdmin()).to.be.equal(marketUpdateTimelockAddress); + expect(await marketAdminPermissionChecker.owner()).to.be.equal(localTimelockAddress); + expect(await marketAdminPermissionChecker.marketAdminPauseGuardian()).to.be.equal(communityMultiSigAddress); + + expect(await marketUpdateTimelock.marketUpdateProposer()).to.be.equal(marketUpdateProposer.address); + expect(await marketUpdateTimelock.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateTimelock.delay()).to.be.equal(2 * 24 * 60 * 60); + + expect(await marketUpdateProposer.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateProposer.marketAdmin()).to.be.equal(marketAdminAddress); + + expect(await marketUpdateProposer.timelock()).to.be.equal(marketUpdateTimelock.address); + expect(await marketUpdateProposer.proposalGuardian()).to.be.equal(communityMultiSigAddress); + + tracer('All checks passed.'); + }, +}); diff --git a/deployments/mainnet/usdc/migrations/1729696345_gov_market_updates.ts b/deployments/mainnet/usdc/migrations/1729696345_gov_market_updates.ts new file mode 100644 index 0000000..324c2eb --- /dev/null +++ b/deployments/mainnet/usdc/migrations/1729696345_gov_market_updates.ts @@ -0,0 +1,176 @@ +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { + MarketAdminPermissionChecker, + MarketUpdateProposer, + MarketUpdateTimelock, + CometProxyAdmin, + Configurator, +} from './../../../../build/types'; +import { expect } from 'chai'; +import { proposal } from '../../../../src/deploy'; +import { Contract } from 'ethers'; + +interface Vars {} + +const marketAdminAddress = '0xA1C7b6d8b4DeD5ee46330C865cC8aeCfB13c8b65'; + +const localTimelockAddress = '0x6d903f6003cca6255D85CcA4D3B5E5146dC33925'; + +const marketUpdateTimelockAddress = '0x81Bc6016Fa365bfE929a51Eec9217B441B598eC6'; +const marketUpdateProposerAddress = '0xB6Ef3AC71E9baCF1F4b9426C149d855Bfc4415F9'; +const newConfiguratorImplementationAddress = '0x371DB45c7ee248dAFf4Dc1FFB67A20faa0ecFE02'; +const newCometProxyAdminAddress = '0x24D86Da09C4Dd64e50dB7501b0f695d030f397aF'; +const marketAdminPermissionCheckerAddress = '0x62DD0452411113404cf9a7fE88A5E6E86f9B71a6'; + +const communityMultiSigAddress = '0xbbf3f1421D886E9b2c5D716B5192aC998af2012c'; + +const configuratorProxyAddress = '0x316f9708bB98af7dA9c68C1C3b5e79039cD336E3'; +const cometProxyUsdcAddress = '0xc3d688B66703497DAA19211EEdff47f25384cdc3'; +const cometProxyUsdtAddress = '0x3Afdc9BCA9213A35503b077a6072F3D0d5AB0840'; +const cometProxyWethAddress = '0xA17581A9E3356d9A858b789D68B4d866e593aE94'; +const cometProxyWstethAddress = '0x3D0bb1ccaB520A66e607822fC55BC921738fAFE3'; + +export default migration('1729696345_gov_market_updates', { + prepare: async () => { + return {}; + }, + + enact: async ( + deploymentManager: DeploymentManager, + ) => { + const trace = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + + const { governor, cometAdmin, configurator } = await deploymentManager.getContracts(); + + const newCometProxyAdmin = (await ethers.getContractAt( + 'CometProxyAdmin', + newCometProxyAdminAddress + )) as Contract; + + const actions = [ + { + contract: cometAdmin, + signature: 'changeProxyAdmin(address,address)', + args: [cometProxyUsdcAddress, newCometProxyAdminAddress], + }, + { + contract: cometAdmin, + signature: 'changeProxyAdmin(address,address)', + args: [cometProxyUsdtAddress, newCometProxyAdminAddress], + }, + { + contract: cometAdmin, + signature: 'changeProxyAdmin(address,address)', + args: [cometProxyWethAddress, newCometProxyAdminAddress], + }, + { + contract: cometAdmin, + signature: 'changeProxyAdmin(address,address)', + args: [cometProxyWstethAddress, newCometProxyAdminAddress], + }, + { + contract: cometAdmin, + signature: 'changeProxyAdmin(address,address)', + args: [configuratorProxyAddress, newCometProxyAdminAddress], + }, + { + contract: newCometProxyAdmin, + signature: 'upgrade(address,address)', + args: [configuratorProxyAddress, newConfiguratorImplementationAddress], + }, + { + contract: configurator, + signature: 'setMarketAdminPermissionChecker(address)', + args: [marketAdminPermissionCheckerAddress], + }, + ]; + + const description = + `DoDAO has been examining various areas where improvements can be made to governance in Compound through tooling or automation. A significant issue raised repeatedly by many members concerns the time and effort required to apply Market Updates. + +Currently, approximately 70-90% of the proposals pertain solely to updating market parameters. Gauntlet uses analytics and algorithms to determine new parameters based on current market conditions. These proposals are highly specific and require unique skills for validation. So far, we have seen minimal participation from other community members or teams in validating these parameters. + +Assuming the total cost of reviewing a proposal (including the effort of all delegates) is $2,000 per proposal, we are spending upwards of $300,000 per year on a process that currently acts more as a friction layer without much safeguard, as these market update proposals are rarely reviewed thoroughly. + +This not only slows down the process but also diverts focus from reviewing essential proposals related to new partnerships, the addition of new chains, and the introduction of assets, etc. + +We propose a parallel process specifically for market updates that can bypass the normal governance lifecycle. This would enable us, as a Compound community, to move faster and concentrate on the most critical decisions. + +This proposal has already been discussed here - https://www.comp.xyz/t/market-updates-alternate-governance-track/5379 + +The forum post includes two solutions, and OpenZeppelin has provided details and feedback on those solutions. + +After discussing with OpenZeppelin, DoDAO and OZ together believe that given the amount of changes, updating the Configurator could be the best solution. OpenZeppelin mentioned a couple of important points in the forum post: + +1. Grant the market admin role to an Safe address, which can be maintained by Gauntlet or other community members. +2. Market Updates to the Configurator will go through a timelock, providing sufficient time for the community to review or even block the market updates via this alternate route. +`; + + 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 false; + }, + + async verify(deploymentManager: DeploymentManager) { + await deploymentManager.spider(); + const tracer = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + + const { configurator } = await deploymentManager.getContracts(); + + const marketAdminPermissionChecker = (await ethers.getContractAt( + 'MarketAdminPermissionChecker', + marketAdminPermissionCheckerAddress + )) as MarketAdminPermissionChecker; + + const marketUpdateTimelock = (await ethers.getContractAt( + 'MarketUpdateTimelock', + marketUpdateTimelockAddress + )) as MarketUpdateTimelock; + + const marketUpdateProposer = (await ethers.getContractAt( + 'MarketUpdateProposer', + marketUpdateProposerAddress + )) as MarketUpdateProposer; + + const cometProxyAdminNew = (await ethers.getContractAt( + 'CometProxyAdmin', + newCometProxyAdminAddress + )) as CometProxyAdmin; + + expect(configurator.address).to.be.equal(configuratorProxyAddress); + expect(await (configurator as Configurator).governor()).to.be.equal(localTimelockAddress); + expect(await (configurator as Configurator).marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionCheckerAddress); + + expect(await cometProxyAdminNew.marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionChecker.address); + expect(await cometProxyAdminNew.owner()).to.be.equal(localTimelockAddress); + + expect(await marketAdminPermissionChecker.marketAdmin()).to.be.equal(marketUpdateTimelockAddress); + expect(await marketAdminPermissionChecker.owner()).to.be.equal(localTimelockAddress); + expect(await marketAdminPermissionChecker.marketAdminPauseGuardian()).to.be.equal(communityMultiSigAddress); + + expect(await marketUpdateTimelock.marketUpdateProposer()).to.be.equal(marketUpdateProposer.address); + expect(await marketUpdateTimelock.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateTimelock.delay()).to.be.equal(2 * 24 * 60 * 60); + + expect(await marketUpdateProposer.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateProposer.marketAdmin()).to.be.equal(marketAdminAddress); + + expect(await marketUpdateProposer.timelock()).to.be.equal(marketUpdateTimelock.address); + expect(await marketUpdateProposer.proposalGuardian()).to.be.equal(communityMultiSigAddress); + + tracer('All checks passed.'); + }, +}); diff --git a/deployments/polygon/usdc/migrations/1729698710_gov_market_updates.ts b/deployments/polygon/usdc/migrations/1729698710_gov_market_updates.ts new file mode 100644 index 0000000..95d4a71 --- /dev/null +++ b/deployments/polygon/usdc/migrations/1729698710_gov_market_updates.ts @@ -0,0 +1,200 @@ +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { + MarketAdminPermissionChecker, + MarketUpdateProposer, + MarketUpdateTimelock, + CometProxyAdmin, + Configurator, +} from './../../../../build/types'; +import { expect } from 'chai'; +import { proposal } from '../../../../src/deploy'; + +interface Vars {} + +const marketAdminAddress = '0x7e14050080306cd36b47DE61ce604b3a1EC70c4e'; + +const localTimelockAddress = '0xCC3E7c85Bb0EE4f09380e041fee95a0caeDD4a02'; + +const marketUpdateTimelockAddress = '0x81Bc6016Fa365bfE929a51Eec9217B441B598eC6'; +const marketUpdateProposerAddress = '0xB6Ef3AC71E9baCF1F4b9426C149d855Bfc4415F9'; +const newConfiguratorImplementationAddress = '0x371DB45c7ee248dAFf4Dc1FFB67A20faa0ecFE02'; +const newCometProxyAdminAddress = '0x24D86Da09C4Dd64e50dB7501b0f695d030f397aF'; +const marketAdminPermissionCheckerAddress = '0x62DD0452411113404cf9a7fE88A5E6E86f9B71a6'; + +const communityMultiSigAddress = '0x8Ab717CAC3CbC4934E63825B88442F5810aAF6e5'; + +const cometProxyAdminOldAddress = '0xd712ACe4ca490D4F3E92992Ecf3DE12251b975F9'; +const configuratorProxyAddress = '0x83E0F742cAcBE66349E3701B171eE2487a26e738'; +const cometProxyUsdcAddress = '0xF25212E676D1F7F89Cd72fFEe66158f541246445'; +const cometProxyUsdtAddress = '0xaeB318360f27748Acb200CE616E389A6C9409a07'; + +export default migration('1729698710_gov_market_updates', { + prepare: async () => { + return {}; + }, + + enact: async ( + deploymentManager: DeploymentManager, + govDeploymentManager: DeploymentManager, + ) => { + const trace = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + const { utils } = ethers; + + const { bridgeReceiver } = await deploymentManager.getContracts(); + + const { fxRoot, governor } = + await govDeploymentManager.getContracts(); + + + const changeProxyAdminForCometProxyUsdcCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [cometProxyUsdcAddress, newCometProxyAdminAddress] + ); + + const changeProxyAdminForCometProxyUsdtCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [cometProxyUsdtAddress, newCometProxyAdminAddress] + ); + + const changeProxyAdminForConfiguratorProxyCalldata = + utils.defaultAbiCoder.encode( + ['address', 'address'], + [configuratorProxyAddress, newCometProxyAdminAddress] + ); + + const upgradeConfiguratorProxyCalldata = utils.defaultAbiCoder.encode( + ['address', 'address'], + [configuratorProxyAddress, newConfiguratorImplementationAddress] + ); + + const setMarketAdminPermissionCheckerForConfiguratorProxyCalldata = + utils.defaultAbiCoder.encode( + ['address'], + [marketAdminPermissionCheckerAddress] + ); + + const l2ProposalData = utils.defaultAbiCoder.encode( + ['address[]', 'uint256[]', 'string[]', 'bytes[]'], + [ + [ + cometProxyAdminOldAddress, + cometProxyAdminOldAddress, + cometProxyAdminOldAddress, + newCometProxyAdminAddress, + configuratorProxyAddress, + ], + [0, 0, 0, 0, 0], + [ + 'changeProxyAdmin(address,address)', + 'changeProxyAdmin(address,address)', + 'changeProxyAdmin(address,address)', + 'upgrade(address,address)', + 'setMarketAdminPermissionChecker(address)', + ], + [ + changeProxyAdminForCometProxyUsdcCalldata, + changeProxyAdminForCometProxyUsdtCalldata, + changeProxyAdminForConfiguratorProxyCalldata, + upgradeConfiguratorProxyCalldata, + setMarketAdminPermissionCheckerForConfiguratorProxyCalldata, + ], + ] + ); + + const mainnetActions = [ + { + contract: fxRoot, + signature: 'sendMessageToChild(address,bytes)', + args: [bridgeReceiver.address, l2ProposalData], + }, + ]; + + const description = + `DoDAO has been examining various areas where improvements can be made to governance in Compound through tooling or automation. A significant issue raised repeatedly by many members concerns the time and effort required to apply Market Updates. + +Currently, approximately 70-90% of the proposals pertain solely to updating market parameters. Gauntlet uses analytics and algorithms to determine new parameters based on current market conditions. These proposals are highly specific and require unique skills for validation. So far, we have seen minimal participation from other community members or teams in validating these parameters. + +Assuming the total cost of reviewing a proposal (including the effort of all delegates) is $2,000 per proposal, we are spending upwards of $300,000 per year on a process that currently acts more as a friction layer without much safeguard, as these market update proposals are rarely reviewed thoroughly. + +This not only slows down the process but also diverts focus from reviewing essential proposals related to new partnerships, the addition of new chains, and the introduction of assets, etc. + +We propose a parallel process specifically for market updates that can bypass the normal governance lifecycle. This would enable us, as a Compound community, to move faster and concentrate on the most critical decisions. + +This proposal has already been discussed here - https://www.comp.xyz/t/market-updates-alternate-governance-track/5379 + +The forum post includes two solutions, and OpenZeppelin has provided details and feedback on those solutions. + +After discussing with OpenZeppelin, DoDAO and OZ together believe that given the amount of changes, updating the Configurator could be the best solution. OpenZeppelin mentioned a couple of important points in the forum post: + +1. Grant the market admin role to an Safe address, which can be maintained by Gauntlet or other community members. +2. Market Updates to the Configurator will go through a timelock, providing sufficient time for the community to review or even block the market updates via this alternate route. +`; + + 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 false; + }, + + async verify(deploymentManager: DeploymentManager) { + await deploymentManager.spider(); + const tracer = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + + const { configurator } = await deploymentManager.getContracts(); + + const marketAdminPermissionChecker = (await ethers.getContractAt( + 'MarketAdminPermissionChecker', + marketAdminPermissionCheckerAddress + )) as MarketAdminPermissionChecker; + + const marketUpdateTimelock = (await ethers.getContractAt( + 'MarketUpdateTimelock', + marketUpdateTimelockAddress + )) as MarketUpdateTimelock; + + const marketUpdateProposer = (await ethers.getContractAt( + 'MarketUpdateProposer', + marketUpdateProposerAddress + )) as MarketUpdateProposer; + + const cometProxyAdminNew = (await ethers.getContractAt( + 'CometProxyAdmin', + newCometProxyAdminAddress + )) as CometProxyAdmin; + + expect(configurator.address).to.be.equal(configuratorProxyAddress); + expect(await (configurator as Configurator).governor()).to.be.equal(localTimelockAddress); + expect(await (configurator as Configurator).marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionCheckerAddress); + + expect(await cometProxyAdminNew.marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionChecker.address); + expect(await cometProxyAdminNew.owner()).to.be.equal(localTimelockAddress); + + expect(await marketAdminPermissionChecker.marketAdmin()).to.be.equal(marketUpdateTimelockAddress); + expect(await marketAdminPermissionChecker.owner()).to.be.equal(localTimelockAddress); + expect(await marketAdminPermissionChecker.marketAdminPauseGuardian()).to.be.equal(communityMultiSigAddress); + + expect(await marketUpdateTimelock.marketUpdateProposer()).to.be.equal(marketUpdateProposer.address); + expect(await marketUpdateTimelock.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateTimelock.delay()).to.be.equal(2 * 24 * 60 * 60); + + expect(await marketUpdateProposer.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateProposer.marketAdmin()).to.be.equal(marketAdminAddress); + + expect(await marketUpdateProposer.timelock()).to.be.equal(marketUpdateTimelock.address); + expect(await marketUpdateProposer.proposalGuardian()).to.be.equal(communityMultiSigAddress); + + tracer('All checks passed.'); + }, +}); diff --git a/deployments/sepolia/usdc/migrations/1729700340_gov_market_updates.ts b/deployments/sepolia/usdc/migrations/1729700340_gov_market_updates.ts new file mode 100644 index 0000000..bd957c6 --- /dev/null +++ b/deployments/sepolia/usdc/migrations/1729700340_gov_market_updates.ts @@ -0,0 +1,164 @@ +import { DeploymentManager } from '../../../../plugins/deployment_manager/DeploymentManager'; +import { migration } from '../../../../plugins/deployment_manager/Migration'; +import { + MarketAdminPermissionChecker, + MarketUpdateProposer, + MarketUpdateTimelock, + CometProxyAdmin, + Configurator, +} from './../../../../build/types'; +import { expect } from 'chai'; +import { proposal } from '../../../../src/deploy'; +import { Contract } from 'ethers'; + +interface Vars {} + +const marketAdminAddress = '0x7e14050080306cd36b47DE61ce604b3a1EC70c4e'; // Need to verify this address + +const localTimelockAddress = '0x6d903f6003cca6255D85CcA4D3B5E5146dC33925'; // currently using mainnet Timelock + +const marketUpdateTimelockAddress = '0x81Bc6016Fa365bfE929a51Eec9217B441B598eC6'; +const marketUpdateProposerAddress = '0xB6Ef3AC71E9baCF1F4b9426C149d855Bfc4415F9'; +const newConfiguratorImplementationAddress = '0x371DB45c7ee248dAFf4Dc1FFB67A20faa0ecFE02'; +const newCometProxyAdminAddress = '0x24D86Da09C4Dd64e50dB7501b0f695d030f397aF'; +const marketAdminPermissionCheckerAddress = '0x62DD0452411113404cf9a7fE88A5E6E86f9B71a6'; + +const communityMultiSigAddress = '0x008a4C5448ac1Df676d6F39A0C6F13b21b189389 '; + +const configuratorProxyAddress = '0xc28aD44975C614EaBe0Ed090207314549e1c6624'; +const cometProxyUsdcAddress = '0xAec1F48e02Cfb822Be958B68C7957156EB3F0b6e'; +const cometProxyWethAddress = '0x2943ac1216979aD8dB76D9147F64E61adc126e96'; + +export default migration('1729700340_gov_market_updates', { + prepare: async () => { + return {}; + }, + + enact: async ( + deploymentManager: DeploymentManager, + govDeploymentManager: DeploymentManager, + ) => { + const trace = deploymentManager.tracer(); + + const { governor, cometAdmin, configurator } = await deploymentManager.getContracts(); + + const newCometProxyAdmin = (await ethers.getContractAt( + 'CometProxyAdmin', + newCometProxyAdminAddress + )) as Contract; + + const actions = [ + { + contract: cometAdmin, + signature: 'changeProxyAdmin(address,address)', + args: [cometProxyUsdcAddress, newCometProxyAdminAddress], + }, + { + contract: cometAdmin, + signature: 'changeProxyAdmin(address,address)', + args: [cometProxyWethAddress, newCometProxyAdminAddress], + }, + { + contract: cometAdmin, + signature: 'changeProxyAdmin(address,address)', + args: [configuratorProxyAddress, newCometProxyAdminAddress], + }, + { + contract: newCometProxyAdmin, + signature: 'upgrade(address,address)', + args: [configuratorProxyAddress, newConfiguratorImplementationAddress], + }, + { + contract: configurator, + signature: 'setMarketAdminPermissionChecker(address)', + args: [marketAdminPermissionCheckerAddress], + }, + ]; + + const description = + `DoDAO has been examining various areas where improvements can be made to governance in Compound through tooling or automation. A significant issue raised repeatedly by many members concerns the time and effort required to apply Market Updates. + +Currently, approximately 70-90% of the proposals pertain solely to updating market parameters. Gauntlet uses analytics and algorithms to determine new parameters based on current market conditions. These proposals are highly specific and require unique skills for validation. So far, we have seen minimal participation from other community members or teams in validating these parameters. + +Assuming the total cost of reviewing a proposal (including the effort of all delegates) is $2,000 per proposal, we are spending upwards of $300,000 per year on a process that currently acts more as a friction layer without much safeguard, as these market update proposals are rarely reviewed thoroughly. + +This not only slows down the process but also diverts focus from reviewing essential proposals related to new partnerships, the addition of new chains, and the introduction of assets, etc. + +We propose a parallel process specifically for market updates that can bypass the normal governance lifecycle. This would enable us, as a Compound community, to move faster and concentrate on the most critical decisions. + +This proposal has already been discussed here - https://www.comp.xyz/t/market-updates-alternate-governance-track/5379 + +The forum post includes two solutions, and OpenZeppelin has provided details and feedback on those solutions. + +After discussing with OpenZeppelin, DoDAO and OZ together believe that given the amount of changes, updating the Configurator could be the best solution. OpenZeppelin mentioned a couple of important points in the forum post: + +1. Grant the market admin role to an Safe address, which can be maintained by Gauntlet or other community members. +2. Market Updates to the Configurator will go through a timelock, providing sufficient time for the community to review or even block the market updates via this alternate route. +`; + + 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(deploymentManager: DeploymentManager): Promise { + return false; + }, + + async verify(deploymentManager: DeploymentManager) { + await deploymentManager.spider(); + const tracer = deploymentManager.tracer(); + const ethers = deploymentManager.hre.ethers; + + const { configurator } = await deploymentManager.getContracts(); + + const marketAdminPermissionChecker = (await ethers.getContractAt( + 'MarketAdminPermissionChecker', + marketAdminPermissionCheckerAddress + )) as MarketAdminPermissionChecker; + + const marketUpdateTimelock = (await ethers.getContractAt( + 'MarketUpdateTimelock', + marketUpdateTimelockAddress + )) as MarketUpdateTimelock; + + const marketUpdateProposer = (await ethers.getContractAt( + 'MarketUpdateProposer', + marketUpdateProposerAddress + )) as MarketUpdateProposer; + + const cometProxyAdminNew = (await ethers.getContractAt( + 'CometProxyAdmin', + newCometProxyAdminAddress + )) as CometProxyAdmin; + + expect(configurator.address).to.be.equal(configuratorProxyAddress); + expect(await (configurator as Configurator).governor()).to.be.equal(localTimelockAddress); + expect(await (configurator as Configurator).marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionCheckerAddress); + + expect(await cometProxyAdminNew.marketAdminPermissionChecker()).to.be.equal(marketAdminPermissionChecker.address); + expect(await cometProxyAdminNew.owner()).to.be.equal(localTimelockAddress); + + expect(await marketAdminPermissionChecker.marketAdmin()).to.be.equal(marketUpdateTimelockAddress); + expect(await marketAdminPermissionChecker.owner()).to.be.equal(localTimelockAddress); + expect(await marketAdminPermissionChecker.marketAdminPauseGuardian()).to.be.equal(communityMultiSigAddress); + + expect(await marketUpdateTimelock.marketUpdateProposer()).to.be.equal(marketUpdateProposer.address); + expect(await marketUpdateTimelock.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateTimelock.delay()).to.be.equal(2 * 24 * 60 * 60); + + expect(await marketUpdateProposer.governor()).to.be.equal(localTimelockAddress); + expect(await marketUpdateProposer.marketAdmin()).to.be.equal(marketAdminAddress); + + expect(await marketUpdateProposer.timelock()).to.be.equal(marketUpdateTimelock.address); + expect(await marketUpdateProposer.proposalGuardian()).to.be.equal(communityMultiSigAddress); + + tracer('All checks passed.'); + }, +});