From adce2194df878cf821dd421c6f3f09061fbabd8f Mon Sep 17 00:00:00 2001 From: David Racero Date: Thu, 22 Apr 2021 16:04:14 +0200 Subject: [PATCH] feat: Added simulation of Defender relayer via hardhat impersonation. Added expected script to submit Incentives proposal --- hardhat.config.ts | 5 +- helper-hardhat-config.ts | 2 +- helpers/defender-utils.ts | 20 +- package-lock.json | 6 + package.json | 4 +- .../deploy-reserve-implementations.ts | 6 +- tasks/migrations/incentives-deploy-mainnet.ts | 223 ++++++++++-------- .../incentives-submit-proposal-mainnet.ts | 115 +++++---- 8 files changed, 220 insertions(+), 161 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 800127a..e9ecd64 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -57,7 +57,7 @@ const getCommonNetworkConfig = (networkName: eNetwork, networkId: number) => ({ const mainnetFork = MAINNET_FORK ? { - blockNumber: 12276538, + blockNumber: 12290275, url: NETWORKS_RPC_URL['main'], } : undefined; @@ -120,7 +120,8 @@ const buidlerConfig: HardhatUserConfig = { hardfork: 'istanbul', blockGasLimit: DEFAULT_BLOCK_GAS_LIMIT, gas: DEFAULT_BLOCK_GAS_LIMIT, - gasPrice: 8000000000, + gasPrice: + NETWORKS_DEFAULT_GAS[MAINNET_FORK ? eEthereumNetwork.main : eEthereumNetwork.hardhat], chainId: BUIDLEREVM_CHAINID, throwOnTransactionFailures: true, throwOnCallFailures: true, diff --git a/helper-hardhat-config.ts b/helper-hardhat-config.ts index 9fa7bb9..c58494a 100644 --- a/helper-hardhat-config.ts +++ b/helper-hardhat-config.ts @@ -36,7 +36,7 @@ export const NETWORKS_RPC_URL: iParamsPerNetwork = { export const NETWORKS_DEFAULT_GAS: iParamsPerNetwork = { [eEthereumNetwork.kovan]: 1 * GWEI, [eEthereumNetwork.ropsten]: 1 * GWEI, - [eEthereumNetwork.main]: 100 * GWEI, + [eEthereumNetwork.main]: 180 * GWEI, [eEthereumNetwork.coverage]: 1 * GWEI, [eEthereumNetwork.hardhat]: 1 * GWEI, [eEthereumNetwork.buidlerevm]: 1 * GWEI, diff --git a/helpers/defender-utils.ts b/helpers/defender-utils.ts index 32e457f..681894a 100644 --- a/helpers/defender-utils.ts +++ b/helpers/defender-utils.ts @@ -1,17 +1,31 @@ +import { formatEther } from '@ethersproject/units'; import { DefenderRelaySigner, DefenderRelayProvider } from 'defender-relay-client/lib/ethers'; +import { Signer } from 'ethers'; +import { DRE, impersonateAccountsHardhat } from './misc-utils'; export const getDefenderRelaySigner = async () => { const { DEFENDER_API_KEY, DEFENDER_SECRET_KEY } = process.env; + let signer: Signer; if (!DEFENDER_API_KEY || !DEFENDER_SECRET_KEY) { throw new Error('Defender secrets required'); } const credentials = { apiKey: DEFENDER_API_KEY, apiSecret: DEFENDER_SECRET_KEY }; - const signer = new DefenderRelaySigner(credentials, new DefenderRelayProvider(credentials), { + + signer = new DefenderRelaySigner(credentials, new DefenderRelayProvider(credentials), { speed: 'fast', }); - const address = await signer.getAddress(); - return { signer, address }; + const defenderAddress = await signer.getAddress(); + + // Reemplace signer if MAINNET_FORK is active + if (process.env.MAINNET_FORK === 'true') { + console.log(' - Impersonating Defender Relay'); + await impersonateAccountsHardhat([defenderAddress]); + signer = await DRE.ethers.getSigner(defenderAddress); + } + console.log(' - Balance: ', formatEther(await signer.getBalance())); + + return { signer, address: defenderAddress }; }; diff --git a/package-lock.json b/package-lock.json index 6ba2ffe..6b6b350 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15650,6 +15650,12 @@ "verror": "1.10.0" } }, + "kebab-case": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/kebab-case/-/kebab-case-1.0.1.tgz", + "integrity": "sha512-txPHx6nVLhv8PHGXIlAk0nYoh894SpAqGPXNvbg2hh8spvHXIah3+vT87DLoa59nKgC6scD3u3xAuRIgiMqbfQ==", + "dev": true + }, "keccak": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", diff --git a/package.json b/package.json index 85c33c7..b3b824f 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "compile": "SKIP_LOAD=true hardhat compile", "console:fork": "MAINNET_FORK=true hardhat console", "prepublishOnly": "npm run compile", - "deploy:incentives-controller-impl:main": "npm run hardhat:main -- deploy-incentives-impl" + "deploy:incentives-controller-impl:main": "npm run hardhat:main -- deploy-incentives-impl", + "submit-proposal:mainner": "npx hardhat --network main incentives-submit-proposal:mainnet --defender --proposal-execution-payload 0x5778DAee2a634acd303dC9dC91e58D57C8FFfcC8 --a-tokens 0x7b2a3CF972C3193F26CdeC6217D27379b6417bD0,0xE994d6d8595741a6245bC3197fD66C10a3E75C5f,0x1C050bCa8BAbe53Ef769d0d2e411f556e1a27E7B,0x3F06560cfB7af6E6B5102c358f679DE5150b3b4C,0xC2fcab14Ec1F2dFA82a23C639c4770345085a50F,0x541dCd3F00Bcd1A683cc73E1b2A8693b602201f4 --variable-debt-tokens 0x3F87b818f94F3cC21e47FD3Bf015E8D8183A3E08,0x4aBF3e82D5f45A8D8E8C48B544bcA562e20EE2ff,0x1f57Cc62113C3a6346882DcF3Ed49120411ac2d2,0x99E81EDbcab512d393638C087fD29c3DC6c9B00E,0x52fdFB1157878f540DCB961561ce5F3b0bbe6f80,0xDddE1FA049209Bc24B69D5fa316a56EfeC918D79" }, "repository": { "type": "git", @@ -65,6 +66,7 @@ "hardhat-gas-reporter": "^1.0.0", "husky": "^4.2.5", "is-ipfs": "^5.0.0", + "kebab-case": "^1.0.1", "lowdb": "1.0.0", "prettier": "^2.0.5", "prettier-plugin-solidity": "^1.0.0-alpha.53", diff --git a/tasks/deployment/deploy-reserve-implementations.ts b/tasks/deployment/deploy-reserve-implementations.ts index f94833d..210e088 100644 --- a/tasks/deployment/deploy-reserve-implementations.ts +++ b/tasks/deployment/deploy-reserve-implementations.ts @@ -51,14 +51,16 @@ task( incentivesController, defender, }); - console.log(`- Deployed ${reserveConfigs[x].symbol} AToken impl`); + console.log(`- Deployed ${reserveConfigs[x].symbol} AToken impl at: ${aTokens[x]}`); variableDebtTokens[x] = await localBRE.run('deploy-var-debt-token', { pool, asset: reserveConfigs[x].tokenAddress, incentivesController, defender, }); - console.log(`- Deployed ${reserveConfigs[x].symbol} Variable Debt Token impl`); + console.log( + `- Deployed ${reserveConfigs[x].symbol} Variable Debt Token impl at: ${variableDebtTokens[x]}` + ); } return { diff --git a/tasks/migrations/incentives-deploy-mainnet.ts b/tasks/migrations/incentives-deploy-mainnet.ts index 26a2450..6e39d77 100644 --- a/tasks/migrations/incentives-deploy-mainnet.ts +++ b/tasks/migrations/incentives-deploy-mainnet.ts @@ -1,10 +1,12 @@ import { task } from 'hardhat/config'; -import { DRE } from '../../helpers/misc-utils'; +import { DRE, impersonateAccountsHardhat } from '../../helpers/misc-utils'; import { tEthereumAddress } from '../../helpers/types'; import { getReserveConfigs } from '../../test-fork/helpers'; import { ProposalIncentivesExecutor__factory, IERC20Detailed__factory } from '../../types'; import { ILendingPool } from '../../types/ILendingPool'; import { getDefenderRelaySigner } from '../../helpers/defender-utils'; +import { Signer } from 'ethers'; +import kebabCase from 'kebab-case'; const { RESERVES = 'DAI,GUSD,USDC,USDT,WBTC,WETH', @@ -24,108 +26,121 @@ const INCENTIVES_PROXY = '0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5'; task( 'incentives-deploy:mainnet', 'Deploy the payload contract, atokens and variable debt token implementations. Print the params for submitting proposal' -).setAction(async (_, localBRE) => { - let aTokensImpl: tEthereumAddress[]; - let variableDebtTokensImpl: tEthereumAddress[]; - let proposalExecutionPayload: tEthereumAddress; - let symbols: { - [key: string]: { - aToken: { symbol: string; name: string }; - variableDebtToken: { symbol: string; name: string }; +) + .addFlag('defender') + .setAction(async ({ defender }, localBRE) => { + let aTokensImpl: tEthereumAddress[]; + let variableDebtTokensImpl: tEthereumAddress[]; + let proposalExecutionPayload: tEthereumAddress; + let symbols: { + [key: string]: { + aToken: { symbol: string; name: string }; + variableDebtToken: { symbol: string; name: string }; + }; + } = {}; + + await localBRE.run('set-DRE'); + + let deployer: Signer; + [deployer] = await DRE.ethers.getSigners(); + + if (defender) { + const { signer } = await getDefenderRelaySigner(); + deployer = signer; + } + + const ethers = DRE.ethers; + + const incentivesProxy = INCENTIVES_PROXY; + + if ( + !RESERVES || + !POOL_CONFIGURATOR || + !POOL_DATA_PROVIDER || + !ECO_RESERVE || + !AAVE_TOKEN || + !AAVE_GOVERNANCE_V2 || + !AAVE_SHORT_EXECUTOR || + !TREASURY + ) { + throw new Error('You have not set correctly the .env file, make sure to read the README.md'); + } + + console.log('- Deploying aTokens and Variable Debt Tokens implementations'); + + // Deploy aTokens and debt tokens + const { aTokens, variableDebtTokens } = await DRE.run('deploy-reserve-implementations', { + provider: POOL_PROVIDER, + assets: RESERVES, + incentivesController: incentivesProxy, + treasury: TREASURY, + defender: true, + }); + + aTokensImpl = [...aTokens]; + variableDebtTokensImpl = [...variableDebtTokens]; + + // Deploy Proposal Executor Payload + const { + address: proposalExecutionPayloadAddress, + } = await new ProposalIncentivesExecutor__factory(deployer).deploy(); + proposalExecutionPayload = proposalExecutionPayloadAddress; + + console.log('Deployed ProposalIncentivesExecutor at:', proposalExecutionPayloadAddress); + + // Initialize contracts and tokens + const pool = (await ethers.getContractAt( + 'ILendingPool', + AAVE_LENDING_POOL, + deployer + )) as ILendingPool; + + // Save aToken and debt token names + const reserveConfigs = await getReserveConfigs(POOL_PROVIDER, RESERVES, deployer); + + for (let x = 0; x < reserveConfigs.length; x++) { + const { tokenAddress, symbol } = reserveConfigs[x]; + const { aTokenAddress, variableDebtTokenAddress } = await pool.getReserveData(tokenAddress); + const aToken = IERC20Detailed__factory.connect(aTokenAddress, deployer); + const varDebtToken = IERC20Detailed__factory.connect(variableDebtTokenAddress, deployer); + + symbols[symbol] = { + aToken: { + name: await aToken.name(), + symbol: await aToken.symbol(), + }, + variableDebtToken: { + name: await varDebtToken.name(), + symbol: await varDebtToken.symbol(), + }, + }; + } + + console.log('- Finished deployment script'); + + console.log('=== INFO ==='); + console.log('Proposal payload:', proposalExecutionPayloadAddress); + console.log('Incentives Controller proxy:', incentivesProxy); + console.log( + 'Needed params to submit the proposal at the following task: ', + '$ npx hardhat --network main incentives-submit-proposal:mainnet' + ); + const proposalParams = { + proposalExecutionPayload, + aTokens: aTokensImpl.join(','), + variableDebtTokens: variableDebtTokensImpl.join(','), }; - } = {}; - - await localBRE.run('set-DRE'); - - const { signer: proposerRelay, address: proposerAddress } = await getDefenderRelaySigner(); - - const ethers = DRE.ethers; - - const incentivesProxy = INCENTIVES_PROXY; - - if ( - !RESERVES || - !POOL_CONFIGURATOR || - !POOL_DATA_PROVIDER || - !ECO_RESERVE || - !AAVE_TOKEN || - !AAVE_GOVERNANCE_V2 || - !AAVE_SHORT_EXECUTOR || - !TREASURY - ) { - throw new Error('You have not set correctly the .env file, make sure to read the README.md'); - } - - console.log('- Deploying aTokens and Variable Debt Tokens implementations'); - - // Deploy aTokens and debt tokens - const { aTokens, variableDebtTokens } = await DRE.run('deploy-reserve-implementations', { - provider: POOL_PROVIDER, - assets: RESERVES, - incentivesController: incentivesProxy, - treasury: TREASURY, - defender: true, + console.log( + `--defender `, + Object.keys(proposalParams) + .map((str) => `--${kebabCase(str)} ${proposalParams[str]}`) + .join(' ') + ); + + await DRE.run('verify-proposal-etherscan', { + assets: RESERVES, + aTokens: aTokensImpl.join(','), + variableDebtTokens: variableDebtTokensImpl.join(','), + proposalPayloadAddress: proposalExecutionPayloadAddress, + }); }); - - aTokensImpl = [...aTokens]; - variableDebtTokensImpl = [...variableDebtTokens]; - - // Deploy Proposal Executor Payload - const { - address: proposalExecutionPayloadAddress, - } = await new ProposalIncentivesExecutor__factory(proposerRelay).deploy(); - proposalExecutionPayload = proposalExecutionPayloadAddress; - - console.log(-'Deployed ProposalIncentivesExecutor at:', proposalExecutionPayloadAddress); - - // Initialize contracts and tokens - const pool = (await ethers.getContractAt( - 'ILendingPool', - AAVE_LENDING_POOL, - proposerRelay - )) as ILendingPool; - - // Save aToken and debt token names - const reserveConfigs = await getReserveConfigs(POOL_PROVIDER, RESERVES, proposerRelay); - - for (let x = 0; x < reserveConfigs.length; x++) { - const { tokenAddress, symbol } = reserveConfigs[x]; - const { aTokenAddress, variableDebtTokenAddress } = await pool.getReserveData(tokenAddress); - const aToken = IERC20Detailed__factory.connect(aTokenAddress, proposerRelay); - const varDebtToken = IERC20Detailed__factory.connect(variableDebtTokenAddress, proposerRelay); - - symbols[symbol] = { - aToken: { - name: await aToken.name(), - symbol: await aToken.symbol(), - }, - variableDebtToken: { - name: await varDebtToken.name(), - symbol: await varDebtToken.symbol(), - }, - }; - } - - console.log('- Finished deployment script'); - - console.log('=== INFO ==='); - console.log('Proposal payload:', proposalExecutionPayloadAddress); - console.log('Incentives Controller proxy:', incentivesProxy); - console.log( - 'Needed params to submit the proposal at the following task: ', - '$ npx hardhat --network main incentives-submit-proposal:mainnet' - ); - const proposalParams = { - proposalExecutionPayload, - aTokens: aTokensImpl.join(','), - variableDebtTokens: variableDebtTokensImpl.join(','), - }; - console.log(JSON.stringify(proposalParams, null, 2)); - - await DRE.run('verify-proposal-etherscan', { - assets: RESERVES, - aTokens: aTokensImpl.join(','), - variableDebtTokens: variableDebtTokensImpl.join(','), - proposalPayloadAddress: proposalExecutionPayloadAddress, - }); -}); diff --git a/tasks/migrations/incentives-submit-proposal-mainnet.ts b/tasks/migrations/incentives-submit-proposal-mainnet.ts index e5b373f..d0d3723 100644 --- a/tasks/migrations/incentives-submit-proposal-mainnet.ts +++ b/tasks/migrations/incentives-submit-proposal-mainnet.ts @@ -1,14 +1,16 @@ import { formatEther } from 'ethers/lib/utils'; import { task } from 'hardhat/config'; -import { DRE, latestBlock } from '../../helpers/misc-utils'; +import { DRE, impersonateAccountsHardhat, latestBlock } from '../../helpers/misc-utils'; import { IERC20__factory, IGovernancePowerDelegationToken__factory } from '../../types'; import { IAaveGovernanceV2 } from '../../types/IAaveGovernanceV2'; import { getDefenderRelaySigner } from '../../helpers/defender-utils'; import isIPFS from 'is-ipfs'; +import { Signer } from '@ethersproject/abstract-signer'; +import kebabCase from 'kebab-case'; const { AAVE_TOKEN = '0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9', - IPFS_HASH = '', // PENDING + IPFS_HASH = 'QmUkPucZ1WUxwGqR979YAKj2UfUsqpSze6MPDcmhtbzmst', // PENDING AAVE_GOVERNANCE_V2 = '0xEC568fffba86c094cf06b22134B23074DFE2252c', // mainnet AAVE_SHORT_EXECUTOR = '0xee56e2b3d491590b5b31738cc34d5232f378a8d5', // mainnet } = process.env; @@ -17,57 +19,74 @@ task('incentives-submit-proposal:mainnet', 'Submit the incentives proposal to Aa .addParam('proposalExecutionPayload') .addParam('aTokens') .addParam('variableDebtTokens') - .setAction(async ({ proposalExecutionPayload, aTokens, variableDebtTokens }, localBRE) => { - await localBRE.run('set-DRE'); + .addFlag('defender') + .setAction( + async ({ defender, proposalExecutionPayload, aTokens, variableDebtTokens }, localBRE) => { + await localBRE.run('set-DRE'); + let proposer: Signer; + [proposer] = await DRE.ethers.getSigners(); - const { signer: proposerRelay, address: proposerAddress } = await getDefenderRelaySigner(); + if (defender) { + const { signer } = await getDefenderRelaySigner(); + proposer = signer; + } - if (!AAVE_TOKEN || !IPFS_HASH || !AAVE_GOVERNANCE_V2 || !AAVE_SHORT_EXECUTOR) { - throw new Error('You have not set correctly the .env file, make sure to read the README.md'); - } + if (!AAVE_TOKEN || !IPFS_HASH || !AAVE_GOVERNANCE_V2 || !AAVE_SHORT_EXECUTOR) { + throw new Error( + 'You have not set correctly the .env file, make sure to read the README.md' + ); + } - // Initialize contracts and tokens - const gov = (await DRE.ethers.getContractAt( - 'IAaveGovernanceV2', - AAVE_GOVERNANCE_V2, - proposerRelay - )) as IAaveGovernanceV2; + if (aTokens.split(',').length !== 6) { + throw new Error('aTokens input param should have 6 elements'); + } - const aave = IERC20__factory.connect(AAVE_TOKEN, proposerRelay); + if (variableDebtTokens.split(',').length !== 6) { + throw new Error('variable debt token param should have 6 elements'); + } - // Balance and proposal power check - const balance = await aave.balanceOf(proposerAddress); - const priorBlock = ((await latestBlock()) - 1).toString(); - const aaveGovToken = IGovernancePowerDelegationToken__factory.connect( - AAVE_TOKEN, - proposerRelay - ); - const propositionPower = await aaveGovToken.getPowerAtBlock(proposerAddress, priorBlock, '1'); + const proposerAddress = await proposer.getAddress(); - console.log('- AAVE Balance proposer', formatEther(balance)); - console.log( - `- Proposition power of ${proposerAddress} at block: ${priorBlock}`, - formatEther(propositionPower) - ); + // Initialize contracts and tokens + const gov = (await DRE.ethers.getContractAt( + 'IAaveGovernanceV2', + AAVE_GOVERNANCE_V2, + proposer + )) as IAaveGovernanceV2; - if (!isIPFS.multihash(IPFS_HASH)) { - console.log('Please check IPFS_HASH env variable due is not valid ipfs multihash.'); - throw Error('IPFS_HASH is not valid'); - } - // Submit proposal - const proposalId = await gov.getProposalsCount(); - const proposalParams = { - proposalExecutionPayload, - aTokens, - variableDebtTokens, - aaveGovernance: AAVE_GOVERNANCE_V2, - shortExecutor: AAVE_SHORT_EXECUTOR, - ipfsHash: IPFS_HASH, - defender: true, - }; - console.log('- Submitting proposal with following params:'); - console.log(JSON.stringify(proposalParams, null, 2)); + const aave = IERC20__factory.connect(AAVE_TOKEN, proposer); + + // Balance and proposal power check + const balance = await aave.balanceOf(proposerAddress); + const priorBlock = ((await latestBlock()) - 1).toString(); + const aaveGovToken = IGovernancePowerDelegationToken__factory.connect(AAVE_TOKEN, proposer); + const propositionPower = await aaveGovToken.getPowerAtBlock(proposerAddress, priorBlock, '1'); - await DRE.run('propose-incentives', proposalParams); - console.log('- Proposal Submited:', proposalId.toString()); - }); + console.log('- AAVE Balance proposer', formatEther(balance)); + console.log( + `- Proposition power of ${proposerAddress} at block: ${priorBlock}`, + formatEther(propositionPower) + ); + + if (!isIPFS.multihash(IPFS_HASH)) { + console.log('Please check IPFS_HASH env variable due is not valid ipfs multihash.'); + throw Error('IPFS_HASH is not valid'); + } + // Submit proposal + const proposalId = await gov.getProposalsCount(); + const proposalParams = { + proposalExecutionPayload, + aTokens, + variableDebtTokens, + aaveGovernance: AAVE_GOVERNANCE_V2, + shortExecutor: AAVE_SHORT_EXECUTOR, + ipfsHash: IPFS_HASH, + defender: true, + }; + console.log('- Submitting proposal with following params:'); + console.log(JSON.stringify(proposalParams, null, 2)); + + await DRE.run('propose-incentives', proposalParams); + console.log('- Proposal Submited:', proposalId.toString()); + } + );