Skip to content

Commit

Permalink
feat: verify with deployment (#3255)
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalaji authored Feb 22, 2024
1 parent 3bd520e commit 7d530fd
Show file tree
Hide file tree
Showing 32 changed files with 1,077 additions and 730 deletions.
13 changes: 13 additions & 0 deletions .changeset/green-pans-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@hyperlane-xyz/helloworld': minor
'@hyperlane-xyz/infra': minor
'@hyperlane-xyz/sdk': minor
'@hyperlane-xyz/core': minor
---

Enabled verification of contracts as part of the deployment flow.

- Solidity build artifact is now included as part of the `@hyperlane-xyz/core` package.
- Updated the `HyperlaneDeployer` to perform contract verification immediately after deploying a contract. A default verifier is instantiated using the core build artifact.
- Updated the `HyperlaneIsmFactory` to re-use the `HyperlaneDeployer` for deployment where possible.
- Minor logging improvements throughout deployers.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM node:18-alpine

WORKDIR /hyperlane-monorepo

RUN apk add --update --no-cache git g++ make py3-pip
RUN apk add --update --no-cache git g++ make py3-pip jq

RUN yarn set version 4.0.1

Expand Down
1 change: 1 addition & 0 deletions solidity/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ out
forge-cache
docs
flattened/
buildArtifact.json
34 changes: 34 additions & 0 deletions solidity/exportBuildArtifact.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/sh

# set script location as working directory
cd "$(dirname "$0")"

# Define the artifacts directory
artifactsDir="./artifacts/build-info"
# Define the output file
outputFile="./buildArtifact.json"

# log that we're in the script
echo 'Finding and processing hardhat build artifact...'

# Find most recently modified JSON build artifact
if [[ $OSTYPE == 'darwin'* ]]; then
# for local flow
jsonFiles=$(find "$artifactsDir" -type f -name "*.json" -exec stat -f "%m %N" {} \; | sort -rn | head -n 1 | cut -d' ' -f2-)
else
# for CI flow
jsonFiles=$(find "$artifactsDir" -type f -name "*.json" -exec stat -c "%Y %n" {} \; | sort -rn | head -n 1 | cut -d' ' -f2-)
fi

if [[ ! -f "$jsonFiles" ]]; then
echo 'Failed to find build artifact'
exit 1
fi

# Extract required keys and write to outputFile
if jq -c '{input, solcLongVersion}' "$jsonFiles" > "$outputFile"; then
echo 'Finished processing build artifact.'
else
echo 'Failed to process build artifact with jq'
exit 1
fi
3 changes: 2 additions & 1 deletion solidity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"test": "test"
},
"files": [
"/buildInfo.json",
"/dist",
"/contracts",
"/interfaces",
Expand All @@ -45,7 +46,7 @@
"main": "dist/index.js",
"repository": "https://github.com/hyperlane-xyz/hyperlane-monorepo",
"scripts": {
"build": "hardhat compile && tsc",
"build": "hardhat compile && ./exportBuildArtifact.sh && tsc",
"lint": "solhint contracts/**/*.sol",
"clean": "hardhat clean && rm -rf ./dist ./cache ./types ./coverage",
"coverage": "./coverage.sh",
Expand Down
2 changes: 1 addition & 1 deletion solidity/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
},
"exclude": ["./test", "hardhat.config.ts", "./dist"],
"extends": "../tsconfig.json"
}
}
7 changes: 6 additions & 1 deletion typescript/helloworld/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ethers } from 'ethers';

import {
ChainName,
ContractVerifier,
HyperlaneContracts,
HyperlaneIsmFactory,
HyperlaneRouterDeployer,
Expand All @@ -20,8 +21,12 @@ export class HelloWorldDeployer extends HyperlaneRouterDeployer<
constructor(
multiProvider: MultiProvider,
readonly ismFactory?: HyperlaneIsmFactory,
readonly contractVerifier?: ContractVerifier,
) {
super(multiProvider, helloWorldFactories, { ismFactory });
super(multiProvider, helloWorldFactories, {
ismFactory,
contractVerifier,
});
}

router(contracts: HyperlaneContracts<HelloWorldFactories>): HelloWorld {
Expand Down
10 changes: 9 additions & 1 deletion typescript/infra/scripts/agent-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function getArgs() {
export function withModuleAndFork<T>(args: yargs.Argv<T>) {
return args
.choices('module', Object.values(Modules))
.demandOption('module')
.demandOption('module', 'hyperlane module to deploy')
.alias('m', 'module')
.describe('fork', 'network to fork')
.choices('fork', Object.values(Chains))
Expand All @@ -88,6 +88,7 @@ export function withContext<T>(args: yargs.Argv<T>) {
.describe('context', 'deploy context')
.default('context', Contexts.Hyperlane)
.coerce('context', assertContext)
.alias('x', 'context')
.demandOption('context');
}

Expand Down Expand Up @@ -133,6 +134,13 @@ export function withMissingChains<T>(args: yargs.Argv<T>) {
.alias('n', 'newChains');
}

export function withBuildArtifactPath<T>(args: yargs.Argv<T>) {
return args
.describe('buildArtifactPath', 'path to hardhat build artifact')
.string('buildArtifactPath')
.alias('b', 'buildArtifactPath');
}

export function assertEnvironment(env: string): DeployEnvironment {
if (EnvironmentNames.includes(env)) {
return env as DeployEnvironment;
Expand Down
62 changes: 51 additions & 11 deletions typescript/infra/scripts/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { prompt } from 'prompts';
import { HelloWorldDeployer } from '@hyperlane-xyz/helloworld';
import {
ChainMap,
ContractVerifier,
ExplorerLicenseType,
HypERC20Deployer,
HyperlaneCore,
HyperlaneCoreDeployer,
Expand All @@ -25,6 +27,10 @@ import { aggregationIsm } from '../config/routingIsm';
import { deployEnvToSdkEnv } from '../src/config/environment';
import { deployWithArtifacts } from '../src/deployment/deploy';
import { TestQuerySenderDeployer } from '../src/deployment/testcontracts/testquerysender';
import {
extractBuildArtifact,
fetchExplorerApiKeys,
} from '../src/deployment/verify';
import { impersonateAccount, useLocalProvider } from '../src/utils/fork';

import {
Expand All @@ -34,6 +40,7 @@ import {
getArgs,
getContractAddressesSdkFilepath,
getModuleDirectory,
withBuildArtifactPath,
withContext,
withModuleAndFork,
withNetwork,
Expand All @@ -47,7 +54,10 @@ async function main() {
fork,
environment,
network,
} = await withContext(withNetwork(withModuleAndFork(getArgs()))).argv;
buildArtifactPath,
} = await withContext(
withNetwork(withModuleAndFork(withBuildArtifactPath(getArgs()))),
).argv;
const envConfig = getEnvironmentConfig(environment);
const env = deployEnvToSdkEnv[environment];

Expand All @@ -63,18 +73,40 @@ async function main() {
multiProvider.setSharedSigner(signer);
}

let contractVerifier;
if (buildArtifactPath) {
// fetch explorer API keys from GCP
const apiKeys = await fetchExplorerApiKeys();
// extract build artifact contents
const buildArtifact = extractBuildArtifact(buildArtifactPath);
// instantiate verifier
contractVerifier = new ContractVerifier(
multiProvider,
apiKeys,
buildArtifact,
ExplorerLicenseType.MIT,
);
}

let config: ChainMap<unknown> = {};
let deployer: HyperlaneDeployer<any, any>;
if (module === Modules.PROXY_FACTORY) {
config = objMap(envConfig.core, (_chain) => true);
deployer = new HyperlaneProxyFactoryDeployer(multiProvider);
deployer = new HyperlaneProxyFactoryDeployer(
multiProvider,
contractVerifier,
);
} else if (module === Modules.CORE) {
config = envConfig.core;
const ismFactory = HyperlaneIsmFactory.fromAddressesMap(
getAddresses(environment, Modules.PROXY_FACTORY),
multiProvider,
);
deployer = new HyperlaneCoreDeployer(multiProvider, ismFactory);
deployer = new HyperlaneCoreDeployer(
multiProvider,
ismFactory,
contractVerifier,
);
} else if (module === Modules.WARP) {
const core = HyperlaneCore.fromEnvironment(env, multiProvider);
const ismFactory = HyperlaneIsmFactory.fromAddressesMap(
Expand Down Expand Up @@ -102,18 +134,22 @@ async function main() {
plumetestnet,
sepolia,
};
deployer = new HypERC20Deployer(multiProvider, ismFactory);
deployer = new HypERC20Deployer(
multiProvider,
ismFactory,
contractVerifier,
);
} else if (module === Modules.INTERCHAIN_GAS_PAYMASTER) {
config = envConfig.igp;
deployer = new HyperlaneIgpDeployer(multiProvider);
deployer = new HyperlaneIgpDeployer(multiProvider, contractVerifier);
} else if (module === Modules.INTERCHAIN_ACCOUNTS) {
const core = HyperlaneCore.fromEnvironment(env, multiProvider);
config = core.getRouterConfig(envConfig.owners);
deployer = new InterchainAccountDeployer(multiProvider);
deployer = new InterchainAccountDeployer(multiProvider, contractVerifier);
} else if (module === Modules.INTERCHAIN_QUERY_SYSTEM) {
const core = HyperlaneCore.fromEnvironment(env, multiProvider);
config = core.getRouterConfig(envConfig.owners);
deployer = new InterchainQueryDeployer(multiProvider);
deployer = new InterchainQueryDeployer(multiProvider, contractVerifier);
} else if (module === Modules.LIQUIDITY_LAYER) {
const core = HyperlaneCore.fromEnvironment(env, multiProvider);
const routerConfig = core.getRouterConfig(envConfig.owners);
Expand All @@ -127,7 +163,7 @@ async function main() {
...routerConfig[chain],
}),
);
deployer = new LiquidityLayerDeployer(multiProvider);
deployer = new LiquidityLayerDeployer(multiProvider, contractVerifier);
} else if (module === Modules.TEST_RECIPIENT) {
const addresses = getAddresses(environment, Modules.CORE);

Expand All @@ -138,7 +174,7 @@ async function main() {
ethers.constants.AddressZero, // ISM is required for the TestRecipientDeployer but onchain if the ISM is zero address, then it uses the mailbox's defaultISM
};
}
deployer = new TestRecipientDeployer(multiProvider);
deployer = new TestRecipientDeployer(multiProvider, contractVerifier);
} else if (module === Modules.TEST_QUERY_SENDER) {
// Get query router addresses
const queryAddresses = getAddresses(
Expand All @@ -148,11 +184,15 @@ async function main() {
config = objMap(queryAddresses, (_c, conf) => ({
queryRouterAddress: conf.router,
}));
deployer = new TestQuerySenderDeployer(multiProvider);
deployer = new TestQuerySenderDeployer(multiProvider, contractVerifier);
} else if (module === Modules.HELLO_WORLD) {
const core = HyperlaneCore.fromEnvironment(env, multiProvider);
config = core.getRouterConfig(envConfig.owners);
deployer = new HelloWorldDeployer(multiProvider);
deployer = new HelloWorldDeployer(
multiProvider,
undefined,
contractVerifier,
);
} else {
console.log(`Skipping ${module}, deployer unimplemented`);
return;
Expand Down
Loading

0 comments on commit 7d530fd

Please sign in to comment.