Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add verification and monitoring to Deploy Campaign #78

Merged
merged 13 commits into from
Nov 27, 2023
Merged
10 changes: 3 additions & 7 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ subtask(TASK_TEST_RUN_MOCHA_TESTS)
// does not work properly locally or in CI, so we
// keep it commented out and uncomment when using DevNet
// locally.
// !!! Uncomment this when using Tenderly DevNet !!!
// !!! Uncomment this when using Tenderly !!!
// tenderly.setup({ automaticVerifications: false });

const config : HardhatUserConfig = {
Expand Down Expand Up @@ -98,21 +98,17 @@ const config : HardhatUserConfig = {
},
networks: {
mainnet: {
url: "https://mainnet.infura.io/v3/97e75e0bbc6a4419a5dd7fe4a518b917",
url: `${process.env.MAINNET_RPC_URL}`,
gasPrice: 80000000000,
},
sepolia: {
url: "https://eth-sepolia.g.alchemy.com/v2/mX2eTgCe-osaWbsEJ1s7sRgmjZ1c178y",
url: `${process.env.SEPOLIA_RPC_URL}`,
timeout: 10000000,
// accounts: [ // Comment out for CI, uncomment this when using Sepolia
// `${process.env.TESTNET_PRIVATE_KEY_A}`,
// `${process.env.TESTNET_PRIVATE_KEY_B}`,
// ]
},
goerli: {
url: "https://goerli.infura.io/v3/77c3d733140f4c12a77699e24cb30c27",
timeout: 10000000,
},
devnet: {
// Add current URL that you spawned if not using automated spawning
url: `${process.env.DEVNET_RPC_URL}`,
Expand Down
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
"lint": "yarn lint-sol & yarn lint-ts --no-error-on-unmatched-pattern",
"clean": "hardhat clean",
"build": "yarn run clean && yarn run compile",
"postbuild": "yarn save-tag-bash",
"postbuild": "yarn save-tag",
"typechain": "hardhat typechain",
"pretest": "yarn mongo:start && yarn save-tag",
"pretest": "yarn mongo:start",
"test": "hardhat test",
"test-local": "yarn test",
"posttest": "yarn mongo:stop",
Expand All @@ -28,8 +28,7 @@
"devnet": "ts-node src/tenderly/devnet/devnet-execute.ts",
"gas-cost": "ts-node src/utils/gas-costs.ts",
"docgen": "hardhat docgen",
"save-tag": "ts-node src/utils/git-tag/save-tag.ts",
"save-tag-bash": "chmod a+x ./src/utils/git-tag/save-tag.sh && bash ./src/utils/git-tag/save-tag.sh",
"save-tag": "chmod a+x ./src/utils/git-tag/save-tag.sh && bash ./src/utils/git-tag/save-tag.sh",
"mongo:start": "docker-compose up -d",
"mongo:stop": "docker-compose stop",
"mongo:down": "docker-compose down",
Expand Down
43 changes: 43 additions & 0 deletions src/deploy/campaign/deploy-campaign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { TDeployMissionCtor } from "../missions/types";
import { BaseDeployMission } from "../missions/base-deploy-mission";
import { Contract } from "ethers";
import { MongoDBAdapter } from "../db/mongo-adapter/mongo-adapter";
import { ContractByName } from "@tenderly/hardhat-tenderly/dist/tenderly/types";


export class DeployCampaign {
Expand Down Expand Up @@ -93,6 +94,14 @@ export class DeployCampaign {
Promise.resolve()
);

if (this.config.postDeploy.verifyContracts) {
await this.verify();
}

if (this.config.postDeploy.monitorContracts) {
await this.monitor();
}

this.logger.debug("Deploy Campaign execution finished successfully.");
}

Expand All @@ -101,4 +110,38 @@ export class DeployCampaign {
// TODO dep: make better logger and decide which levels to call where
this.logger.debug(`Data of deployed contract '${contractName}' is added to Campaign state at '${instanceName}'.`);
}

async verify () {
return Object.values(this.state.instances).reduce(
async (
acc : Promise<void>,
missionInstance : BaseDeployMission,
) => {
await acc;
return missionInstance.verify();
},
Promise.resolve()
);
}

async monitor () {
this.logger.info("Pushing contracts to Tenderly...");

const contracts = await Object.values(this.state.instances).reduce(
async (
acc : Promise<Array<ContractByName>>,
missionInstance : BaseDeployMission,
) : Promise<Array<ContractByName>> => {
const newAcc = await acc;
const data = await missionInstance.getMonitoringData();

return [...newAcc, ...data];
},
Promise.resolve([])
);

await this.deployer.tenderlyVerify(contracts);

this.logger.info(`Tenderly push finished successfully for Project ${this.config.postDeploy.tenderlyProjectSlug}.`);
}
}
17 changes: 16 additions & 1 deletion src/deploy/campaign/environments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ export const getConfig = (
zeroVaultAddress: process.env.ZERO_VAULT_ADDRESS ? process.env.ZERO_VAULT_ADDRESS : zeroVault.address,
mockMeowToken: process.env.MOCK_MEOW_TOKEN ? !!process.env.MOCK_MEOW_TOKEN : true,
stakingTokenAddress: process.env.STAKING_TOKEN_ADDRESS ? process.env.STAKING_TOKEN_ADDRESS : MeowMainnet.address,
postDeploy: {
tenderlyProjectSlug: process.env.TENDERLY_PROJECT_SLUG ? process.env.TENDERLY_PROJECT_SLUG : "",
monitorContracts: process.env.MONITOR_CONTRACTS === "true",
verifyContracts: process.env.VERIFY_CONTRACTS === "true",
},
};

// Will throw an error based on any invalid setup, given the `ENV_LEVEL` set
Expand Down Expand Up @@ -159,9 +164,19 @@ export const validate = (
requires(config.stakingTokenAddress === MeowMainnet.address, STAKING_TOKEN_ERR);
requires(validatePrice(config.rootPriceConfig), INVALID_CURVE_ERR);
requires(!mongoUri.includes("localhost"), MONGO_URI_ERR);

if (config.postDeploy.verifyContracts) {
requires(!!process.env.ETHERSCAN_API_KEY, "Must provide an Etherscan API Key to verify contracts");
}

if (config.postDeploy.monitorContracts) {
requires(!!process.env.TENDERLY_PROJECT_SLUG, "Must provide a Tenderly Project Slug to monitor contracts");
requires(!!process.env.TENDERLY_ACCOUNT_ID, "Must provide a Tenderly Account ID to monitor contracts");
requires(!!process.env.TENDERLY_ACCESS_KEY, "Must provide a Tenderly Access Key to monitor contracts");
}
}

// If we reach this code, there is an env variable but it's not valid.
// If we reach this code, there is an env variable, but it's not valid.
throw new Error(INVALID_ENV_ERR);
};

Expand Down
5 changes: 5 additions & 0 deletions src/deploy/campaign/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ export interface IDeployCampaignConfig {
zeroVaultAddress : string;
mockMeowToken : boolean;
stakingTokenAddress : string;
postDeploy : {
tenderlyProjectSlug : string;
monitorContracts : boolean;
verifyContracts : boolean;
};
}

export type TLogger = WinstonLogger | Console;
Expand Down
20 changes: 20 additions & 0 deletions src/deploy/deployer/hardhat-deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as hre from "hardhat";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { TDeployArgs, TProxyKind } from "../missions/types";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { ContractByName } from "@tenderly/hardhat-tenderly/dist/tenderly/types";

export class HardhatDeployer {
hre : HardhatRuntimeEnvironment;
Expand Down Expand Up @@ -61,4 +62,23 @@ export class HardhatDeployer {
async getBytecodeFromChain (address : string) {
return this.hre.ethers.provider.getCode(address);
}

async tenderlyVerify (contracts : Array<ContractByName>) {
return this.hre.tenderly.verify(...contracts);
}

async etherscanVerify ({
address,
ctorArgs,
} : {
address : string;
ctorArgs ?: TDeployArgs;
}) {
return this.hre.run("verify:verify", {
address,
// this should only be used for non-proxied contracts
// or proxy impls that have actual constructors
constructorArguments: ctorArgs,
});
}
}
51 changes: 49 additions & 2 deletions src/deploy/missions/base-deploy-mission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import {
import { DeployCampaign } from "../campaign/deploy-campaign";
import { IDeployCampaignConfig, TLogger } from "../campaign/types";
import { IContractDbData } from "../db/types";
import { erc1967ProxyName, transparentProxyName } from "./contracts/names";
import { ProxyKinds } from "../constants";
import { ContractByName } from "@tenderly/hardhat-tenderly/dist/tenderly/types";


// TODO dep:
Expand All @@ -19,6 +22,7 @@ export class BaseDeployMission {
campaign : DeployCampaign;
logger : TLogger;
config : IDeployCampaignConfig;
implAddress! : string | null;

constructor ({
campaign,
Expand All @@ -37,11 +41,11 @@ export class BaseDeployMission {
async saveToDB (contract : Contract) {
this.logger.debug(`Writing ${this.contractName} to DB...`);

const implAddress = this.proxyData.isProxy
this.implAddress = this.proxyData.isProxy
? await this.campaign.deployer.getProxyImplAddress(contract.address)
: null;

const contractDbDoc = this.buildDbObject(contract, implAddress);
const contractDbDoc = this.buildDbObject(contract, this.implAddress);

return this.campaign.dbAdapter.writeContract(this.contractName, contractDbDoc);
}
Expand Down Expand Up @@ -136,4 +140,47 @@ export class BaseDeployMission {
await this.postDeploy();
}
}

async verify () {
this.logger.info(`Verifying ${this.contractName} on Etherscan...`);
const { address } = await this.campaign[this.instanceName];

const ctorArgs = !this.proxyData.isProxy ? this.deployArgs() : undefined;

await this.campaign.deployer.etherscanVerify({
address,
ctorArgs,
});

this.logger.info(`Etherscan verification for ${this.contractName} finished successfully.`);
}

async getMonitoringData () : Promise<Array<ContractByName>> {
const implName = this.contractName;
let implAddress = this.campaign[this.instanceName].address;

if (this.proxyData.isProxy) {
const proxyName = this.proxyData.kind === ProxyKinds.uups ? erc1967ProxyName : transparentProxyName;
const proxyAddress = this.campaign[this.instanceName].address;
implAddress = this.implAddress || await this.campaign.deployer.getProxyImplAddress(proxyAddress);

return [
{
name: proxyName,
address: proxyAddress,
},
{
name: implName,
address: implAddress,
},
];
}

return [
{
name: implName,
address: implAddress,
},
];
}
}
2 changes: 2 additions & 0 deletions src/deploy/missions/contracts/names.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export const erc1967ProxyName = "ERC1967Proxy";
export const transparentProxyName = "TransparentUpgradeableProxy";


export const znsNames = {
accessController: {
Expand Down
3 changes: 1 addition & 2 deletions src/deploy/run-campaign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ const runCampaign = async () => {
const [deployer, zeroVault] = await hre.ethers.getSigners();

// Reading `ENV_LEVEL` environment variable to determine rules to be enforced
const config = await getConfig(
const config = getConfig(
deployer,
zeroVault,
);

await runZnsCampaign({
config,
logger,
});
};

Expand Down
4 changes: 3 additions & 1 deletion src/deploy/zns-campaign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@ import { getLogger } from "./logger/create-logger";
export const runZnsCampaign = async ({
config,
dbVersion,
deployer,
} : {
config : IDeployCampaignConfig;
dbVersion ?: string;
deployer ?: HardhatDeployer;
}) => {
// TODO dep: figure out the best place to put this at!
hre.upgrades.silenceWarnings();

const logger = getLogger();

const deployer = new HardhatDeployer(config.deployAdmin);
if (!deployer) deployer = new HardhatDeployer(config.deployAdmin);

const dbAdapter = await getMongoAdapter();

Expand Down
12 changes: 2 additions & 10 deletions src/utils/git-tag/save-tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const execAsync = promisify(exec);
const logger = getLogger();


const acquireLatestGitTag = async () => {
export const acquireLatestGitTag = async () => {
const gitTag = await execAsync("git describe --tags --abbrev=0");
const tag = gitTag.stdout.trim();

Expand All @@ -24,17 +24,9 @@ const acquireLatestGitTag = async () => {
return full;
};

const saveTag = async () => {
export const saveTag = async () => {
const tag = await acquireLatestGitTag();

fs.writeFileSync(tagFilePath, tag, "utf8");
logger.info(`Saved git tag-commit to ${tagFilePath}}`);
};

saveTag()
// eslint-disable-next-line @typescript-eslint/no-empty-function
.then(process.exit(0))
.catch(e => {
logger.error(e);
process.exit(1);
});
6 changes: 3 additions & 3 deletions tenderly.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
account_id: e750f9f2-65bc-4765-84a8-7f8dbddd73ee
account_id: "zer0-os"
project_slug: "zns-sepolia-test"
provider: ""
exports:
hardhat:
project_slug: zer0-os/zns-1
Expand All @@ -17,5 +19,3 @@ exports:
istanbul_block: 0
berlin_block: 0
london_block: 0
project_slug: zer0-os/zns-1
provider: ""
Loading